diff --git a/.eslintignore b/.eslintignore index f92ffc84b..9cc0cd9ea 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,5 +11,4 @@ # production /build - /public diff --git a/.eslintrc.json b/.eslintrc.json index 6960b265a..0cc6fa65d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,14 +4,7 @@ "es2021": true, "node": true }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - "plugin:jsx-a11y/recommended", - "plugin:prettier/recommended" - ], + "extends": ["plugin:jsx-a11y/recommended", "@lidofinance", "prettier"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { @@ -22,11 +15,13 @@ }, "plugins": ["@typescript-eslint", "react"], "rules": { - "prettier/prettier": ["error", {}, { "usePrettierrc": true }], - "react/react-in-jsx-scope": "off", - "react/prop-types": "off", + "@typescript-eslint/require-await": "off", "react/display-name": "off", - "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-shadow": "off", + "jsx-a11y/no-autofocus": "off", + "jsx-a11y/anchor-is-valid": "off", + "@next/next/no-img-element": "off", + "no-console": ["warn", { "allow": ["warn", "error", "info", "debug"] }], "@typescript-eslint/no-unused-vars": [ "warn", { @@ -34,9 +29,20 @@ "argsIgnorePattern": "^_" } ], - "jsx-a11y/no-autofocus": "off", - "jsx-a11y/anchor-is-valid": "off", - "func-style": ["error", "expression"] + "promise/param-names": [ + "warn", + { + "resolvePattern": "^_?(resolve)$|^_$", + "rejectPattern": "^_?(reject)$|^_$" + } + ], + "func-style": ["error", "expression"], + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ] }, "settings": { "react": { diff --git a/features/home/stake-form/hooks.ts b/features/home/stake-form/hooks.ts index 2a40cfdef..a8f605da9 100644 --- a/features/home/stake-form/hooks.ts +++ b/features/home/stake-form/hooks.ts @@ -1,15 +1,11 @@ import { AddressZero } from '@ethersproject/constants'; import { useLidoSWR, useSTETHContractRPC } from '@lido-sdk/react'; -import { - ESTIMATE_ACCOUNT, - getBackendRPCPath, - STETH_SUBMIT_GAS_LIMIT_DEFAULT, -} from 'config'; +import { ESTIMATE_ACCOUNT, STETH_SUBMIT_GAS_LIMIT_DEFAULT } from 'config'; import { parseEther } from '@ethersproject/units'; import { useWeb3 } from 'reef-knot/web3-react'; -import { getStaticRpcBatchProvider } from 'utils/rpcProviders'; import { BigNumber } from 'ethers'; -import { CHAINS } from 'utils/chains'; +import { getFeeData } from 'utils/getFeeData'; +import { CHAINS } from '@lido-sdk/constants'; type UseStethSubmitGasLimit = () => number | undefined; @@ -24,13 +20,7 @@ export const useStethSubmitGasLimit: UseStethSubmitGasLimit = () => { return; } - const provider = getStaticRpcBatchProvider( - chainId as string, - // TODO: add a way to type useWeb3 hook - getBackendRPCPath(chainId as CHAINS), - ); - - const feeData = await provider.getFeeData(); + const feeData = await getFeeData(chainId as CHAINS); const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; const maxFeePerGas = feeData.maxFeePerGas ?? undefined; diff --git a/features/home/stake-form/stake-form.tsx b/features/home/stake-form/stake-form.tsx index 9723192bb..e216f0df3 100644 --- a/features/home/stake-form/stake-form.tsx +++ b/features/home/stake-form/stake-form.tsx @@ -61,7 +61,7 @@ export const StakeForm: FC = memo(() => { typeof router.query.amount === 'string' ) { const { amount, ...rest } = router.query; - router.replace({ pathname: router.pathname, query: rest }); + void router.replace({ pathname: router.pathname, query: rest }); setInputValue(amount); } }, [router]); @@ -97,7 +97,6 @@ export const StakeForm: FC = memo(() => { providerWeb3, stethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -113,7 +112,6 @@ export const StakeForm: FC = memo(() => { providerWeb3, stethContractWeb3, openTxModal, - closeTxModal, stethBalance.update, chainId, router?.query?.ref, diff --git a/features/home/stake-form/utils.ts b/features/home/stake-form/utils.ts index 0a8be08e1..4073ba4dd 100644 --- a/features/home/stake-form/utils.ts +++ b/features/home/stake-form/utils.ts @@ -14,6 +14,7 @@ import { getBackendRPCPath } from 'config'; import { TX_STAGE } from 'shared/components'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; +import { getFeeData } from 'utils/getFeeData'; import type { Web3Provider } from '@ethersproject/providers'; const SUBMIT_EXTRA_GAS_TRANSACTION_RATIO = 1.05; @@ -22,7 +23,6 @@ type StakeProcessingProps = ( providerWeb3: Web3Provider | undefined, stethContractWeb3: StethAbi | null, openTxModal: () => void, - closeTxModal: () => void, setTxStage: (value: TX_STAGE) => void, setTxHash: (value: string | undefined) => void, setTxModalFailedText: (value: string) => void, @@ -68,7 +68,6 @@ export const stakeProcessing: StakeProcessingProps = async ( providerWeb3, stethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -79,15 +78,12 @@ export const stakeProcessing: StakeProcessingProps = async ( refFromQuery, isMultisig, ) => { - if (!stethContractWeb3 || !chainId) { - return; - } - - invariant(providerWeb3, 'must have providerWeb3'); - try { - const referralAddress = await getAddress(refFromQuery, chainId); + invariant(stethContractWeb3); + invariant(chainId); + invariant(providerWeb3); + const referralAddress = await getAddress(refFromQuery, chainId); const callback = async () => { if (isMultisig) { const tx = await stethContractWeb3.populateTransaction.submit( @@ -98,17 +94,13 @@ export const stakeProcessing: StakeProcessingProps = async ( ); return providerWeb3.getSigner().sendUncheckedTransaction(tx); } else { - const provider = getStaticRpcBatchProvider( - chainId, - getBackendRPCPath(chainId), - ); - - const feeData = await provider.getFeeData(); - + const feeData = await getFeeData(chainId); + const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; + const maxFeePerGas = feeData.maxFeePerGas ?? undefined; const overrides = { value: parseEther(inputValue), - maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, - maxFeePerGas: feeData.maxFeePerGas ?? undefined, + maxPriorityFeePerGas, + maxFeePerGas, }; const originalGasLimit = await stethContractWeb3.estimateGas.submit( @@ -148,12 +140,13 @@ export const stakeProcessing: StakeProcessingProps = async ( ); const handleEnding = () => { + openTxModal(); resetForm(); stethBalanceUpdate(); }; if (isMultisig) { - closeTxModal(); + setTxStage(TX_STAGE.SUCCESS_MULTISIG); handleEnding(); return; } @@ -168,7 +161,6 @@ export const stakeProcessing: StakeProcessingProps = async ( } setTxStage(TX_STAGE.SUCCESS); - openTxModal(); handleEnding(); /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ } catch (error: any) { diff --git a/features/rewards/components/addressInput/AddressInput.tsx b/features/rewards/components/addressInput/AddressInput.tsx index e7b5e1d45..6a5598daa 100644 --- a/features/rewards/components/addressInput/AddressInput.tsx +++ b/features/rewards/components/addressInput/AddressInput.tsx @@ -23,7 +23,7 @@ export const AddressInput: FC = (props) => { } rightDecorator={address ? : null} spellCheck="false" - error={!!inputValue.length && !isValidAnyAddress(inputValue)} + error={inputValue.length > 0 && !isValidAnyAddress(inputValue)} /> ); }; diff --git a/features/rewards/components/rewardsListContent/RewardsListContent.tsx b/features/rewards/components/rewardsListContent/RewardsListContent.tsx index 663a7b49d..28782a615 100644 --- a/features/rewards/components/rewardsListContent/RewardsListContent.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListContent.tsx @@ -32,7 +32,7 @@ export const RewardsListContent: FC = () => { ); } if (error) return ; - if (data && !data.events.length) return ; + if (data && data.events.length === 0) return ; return ( diff --git a/features/rewards/components/stats/Stats.tsx b/features/rewards/components/stats/Stats.tsx index f6b350798..6a123d640 100644 --- a/features/rewards/components/stats/Stats.tsx +++ b/features/rewards/components/stats/Stats.tsx @@ -43,7 +43,7 @@ export const Stats: FC = (props) => { }, []); useEffect(() => { - getStEthEth(); + void getStEthEth(); }, [getStEthEth]); const stEthBalanceParsed = steth.data && new Big(steth.data.toString()); diff --git a/features/rewards/hooks/useGetCurrentAddress.ts b/features/rewards/hooks/useGetCurrentAddress.ts index 2e9844693..39cb017be 100644 --- a/features/rewards/hooks/useGetCurrentAddress.ts +++ b/features/rewards/hooks/useGetCurrentAddress.ts @@ -47,7 +47,7 @@ export const useGetCurrentAddress: UseGetCurrentAddress = () => { ); useEffect(() => { - resolveInputValue(inputValue); + void resolveInputValue(inputValue); }, [resolveInputValue, inputValue]); // Pick up an address diff --git a/features/rewards/utils/saveAsCSV.ts b/features/rewards/utils/saveAsCSV.ts index f108bd40e..71891a68d 100644 --- a/features/rewards/utils/saveAsCSV.ts +++ b/features/rewards/utils/saveAsCSV.ts @@ -12,7 +12,7 @@ const objToCSV = (objArray: SomeObj[]) => { }; export const saveAsCSV = (data: SomeObj[], fileName = 'Lido Rewards') => { - if (!data.length) { + if (data.length === 0) { return; } diff --git a/features/withdrawals/claim/form/claim-form.tsx b/features/withdrawals/claim/form/claim-form.tsx index d3b8e11e0..6bdfe0686 100644 --- a/features/withdrawals/claim/form/claim-form.tsx +++ b/features/withdrawals/claim/form/claim-form.tsx @@ -1,5 +1,5 @@ import { useCallback, useRef, useState } from 'react'; -import { useWeb3 } from '@reef-knot/web3-react'; +import { useWeb3 } from 'reef-knot/web3-react'; import { BigNumber } from 'ethers'; import { FormatToken } from 'shared/formatters'; @@ -36,7 +36,7 @@ export const ClaimForm = () => { const startTx = async () => { setIsSubmitting(true); try { - claimMutation(claimSelection.sortedSelectedRequests); + await claimMutation(claimSelection.sortedSelectedRequests); } finally { setIsSubmitting(false); } @@ -44,8 +44,7 @@ export const ClaimForm = () => { // send it to state dispatchModalState({ type: 'set_starTx_callback', callback: startTx }); // start flow - startTx(); - return; + return startTx(); }, [ dispatchModalState, claimMutation, diff --git a/features/withdrawals/claim/requests-list/requests-loader.tsx b/features/withdrawals/claim/requests-list/requests-loader.tsx index 6a2d92799..9542276a5 100644 --- a/features/withdrawals/claim/requests-list/requests-loader.tsx +++ b/features/withdrawals/claim/requests-list/requests-loader.tsx @@ -6,7 +6,9 @@ import { REQUESTS_LIST_LOADERS_COUNT, } from './styles'; -const LOADERS_SIZE_ARRAY = Array.from(Array(REQUESTS_LIST_LOADERS_COUNT)); +const LOADERS_SIZE_ARRAY = Array.from({ + length: REQUESTS_LIST_LOADERS_COUNT, +}).fill(null); export const RequestsLoader = () => { return ( diff --git a/features/withdrawals/claim/tx-modal/tx-claim-modal.tsx b/features/withdrawals/claim/tx-modal/tx-claim-modal.tsx index b98bbd9ec..b3851169b 100644 --- a/features/withdrawals/claim/tx-modal/tx-claim-modal.tsx +++ b/features/withdrawals/claim/tx-modal/tx-claim-modal.tsx @@ -5,6 +5,7 @@ import { TxStageModal, TxStagePending, TxStageSuccess, + TxStageSuccessMultisig, TxStageSign, TxStageFail, TX_STAGE, @@ -65,6 +66,8 @@ export const TxClaimModal = () => { } /> ); + case TX_STAGE.SUCCESS_MULTISIG: + return ; case TX_STAGE.FAIL: return ( = ({ useWithdrawalsBaseData(); const { isBunker, isPaused, isTurbo, maxAmount, minAmount } = data ?? {}; - const withdrawalsStatus: StatusProps['variant'] = isPaused - ? 'error' - : isBunker - ? 'warning' - : isTurbo - ? 'success' - : 'error'; + const withdrawalsStatus: StatusProps['variant'] = (() => { + if (isPaused) return 'error'; + if (isBunker) return 'warning'; + if (isTurbo) return 'success'; + return 'error'; + })(); const value = useMemo( () => ({ diff --git a/features/withdrawals/hooks/contract/useClaim.ts b/features/withdrawals/hooks/contract/useClaim.ts index 0cd737e65..ceb83dee5 100644 --- a/features/withdrawals/hooks/contract/useClaim.ts +++ b/features/withdrawals/hooks/contract/useClaim.ts @@ -85,7 +85,9 @@ export const useClaim = () => { ); } await update(); - dispatchModalState({ type: isMultisig ? 'reset' : 'success' }); + dispatchModalState({ + type: isMultisig ? 'success_multisig' : 'success', + }); } catch (error) { const errorMessage = getErrorMessage(error); dispatchModalState({ type: 'error', errorText: errorMessage }); diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index 32ca0be5e..db2e904d1 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -23,6 +23,7 @@ import { useWithdrawals } from 'features/withdrawals/contexts/withdrawals-contex import { useWithdrawalsContract } from './useWithdrawalsContract'; import { useApprove } from 'shared/hooks/useApprove'; +import { getFeeData } from 'utils/getFeeData'; import { Zero } from '@ethersproject/constants'; import { TokensWithdrawable } from 'features/withdrawals/types/tokens-withdrawable'; @@ -58,7 +59,7 @@ const useWithdrawalRequestMethods = () => { }, ] as const; - const feeData = await contractWeb3.provider.getFeeData(); + const feeData = await getFeeData(chainId); const maxFeePerGas = feeData.maxFeePerGas ?? undefined; const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; const gasLimit = @@ -114,7 +115,7 @@ const useWithdrawalRequestMethods = () => { }, ] as const; - const feeData = await contractWeb3.provider.getFeeData(); + const feeData = await getFeeData(chainId); const maxFeePerGas = feeData.maxFeePerGas ?? undefined; const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; const gasLimit = @@ -169,7 +170,7 @@ const useWithdrawalRequestMethods = () => { ); return providerWeb3?.getSigner().sendUncheckedTransaction(tx); } else { - const feeData = await contractWeb3.provider.getFeeData(); + const feeData = await getFeeData(chainId); const maxFeePerGas = feeData.maxFeePerGas ?? undefined; const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; @@ -224,7 +225,7 @@ const useWithdrawalRequestMethods = () => { ); return providerWeb3?.getSigner().sendUncheckedTransaction(tx); } else { - const feeData = await contractWeb3.provider.getFeeData(); + const feeData = await getFeeData(chainId); const maxFeePerGas = feeData.maxFeePerGas ?? undefined; const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; @@ -352,9 +353,6 @@ export const useWithdrawalRequest = ({ }); if (!bunkerDialogResult) return { success: false }; } - // we can't know if tx was successful or even wait for it with multisig - // so we exit flow gracefully and reset UI - const shouldSkipSuccess = isMultisig; // get right method const method = getRequestMethod(isApprovalFlow, token); // start flow @@ -383,7 +381,9 @@ export const useWithdrawalRequest = ({ await method({ signature, requests }); } // end flow - dispatchModalState({ type: shouldSkipSuccess ? 'reset' : 'success' }); + dispatchModalState({ + type: isMultisig ? 'success_multisig' : 'success', + }); return { success: true }; } catch (error) { const errorMessage = getErrorMessage(error); diff --git a/features/withdrawals/hooks/useWaitingTime.ts b/features/withdrawals/hooks/useWaitingTime.ts index 8d5e3c728..5142ed658 100644 --- a/features/withdrawals/hooks/useWaitingTime.ts +++ b/features/withdrawals/hooks/useWaitingTime.ts @@ -34,17 +34,15 @@ export const useWaitingTime = ( const url = useMemo(() => { const basePath = dynamics.wqAPIBasePath; const params = encodeURLQuery({ amount: debouncedAmount }); - - return `${basePath}/v1/request-time${params ? `?${params}` : ''}`; + const queryString = params ? `?${params}` : ''; + return `${basePath}/v1/request-time${queryString}`; }, [debouncedAmount]); const { data, initialLoading, error } = useLidoSWR(url, standardFetcher, { ...STRATEGY_EAGER, shouldRetryOnError: (e: unknown) => { // if api is not happy about our request - no retry - if (e && typeof e == 'object' && 'status' in e && e.status == 400) - return false; - return true; + return !(e && typeof e == 'object' && 'status' in e && e.status == 400); }, }) as SWRResponse; const { isBunker, isPaused } = useWithdrawals(); diff --git a/features/withdrawals/hooks/useWithdrawTxPrice.ts b/features/withdrawals/hooks/useWithdrawTxPrice.ts index e2e5e3428..667bc8078 100644 --- a/features/withdrawals/hooks/useWithdrawTxPrice.ts +++ b/features/withdrawals/hooks/useWithdrawTxPrice.ts @@ -12,7 +12,7 @@ import { } from 'config'; import { MAX_REQUESTS_COUNT } from 'features/withdrawals/withdrawals-constants'; -import { useWeb3 } from '@reef-knot/web3-react'; +import { useWeb3 } from 'reef-knot/web3-react'; import { TOKENS } from '@lido-sdk/constants'; import { useWithdrawalsContract } from './contract/useWithdrawalsContract'; @@ -74,7 +74,9 @@ export const useRequestTxPrice = ({ invariant(contractRpc, 'contractRpc is required'); const gasLimit = ( await contractRpc.estimateGas.requestWithdrawals( - Array(debouncedRequestCount).fill(BigNumber.from(100)), + Array.from({ length: debouncedRequestCount }).fill( + BigNumber.from(100), + ), ESTIMATE_ACCOUNT, { from: ESTIMATE_ACCOUNT }, ) diff --git a/features/withdrawals/request/form/options/dex-options.tsx b/features/withdrawals/request/form/options/dex-options.tsx index 9202f7ab9..70cfd35aa 100644 --- a/features/withdrawals/request/form/options/dex-options.tsx +++ b/features/withdrawals/request/form/options/dex-options.tsx @@ -22,7 +22,7 @@ import { DexOptionLoader, } from './styles'; -const placeholder = Array(3).fill(null); +const placeholder = Array.from({ length: 3 }).fill(null); const dexInfo: { [key: string]: { @@ -99,9 +99,8 @@ const DexOption: React.FC = ({ Go to {title} - {loading ? ( - - ) : toReceive ? ( + {loading && } + {toReceive ? ( ({ length: requestCount }).fill( + amountPerRequest, + ); if (restCount) { requests[requestCount - 1] = lastRequestAmountEther; } diff --git a/features/withdrawals/request/tx-modal/tx-request-modal.tsx b/features/withdrawals/request/tx-modal/tx-request-modal.tsx index 0b9b59b80..1314be2af 100644 --- a/features/withdrawals/request/tx-modal/tx-request-modal.tsx +++ b/features/withdrawals/request/tx-modal/tx-request-modal.tsx @@ -8,6 +8,7 @@ import { TxStagePermit, TxStageFail, TxStageBunker, + TxStageSuccessMultisig, TX_STAGE, } from 'features/withdrawals/shared/tx-stage-modal'; import { useTransactionModal } from 'features/withdrawals/contexts/transaction-modal-context'; @@ -67,6 +68,8 @@ export const TxRequestModal = () => { amountAsString={amountAsString} /> ); + case TX_STAGE.SUCCESS_MULTISIG: + return ; case TX_STAGE.FAIL: return ( { isPaused, } = useWithdrawals(); - const modeLabel = isBunker - ? 'Bunker' - : isPaused - ? 'Paused' - : isTurbo - ? 'Turbo' - : '-'; + const modeLabel = (() => { + if (isPaused) return 'Paused'; + if (isBunker) return 'Bunker'; + if (isTurbo) return 'Turbo'; + return '-'; + })(); const content = {modeLabel}; const timeTitle = <>Withdrawals mode {}; diff --git a/features/withdrawals/request/wallet/wallet.tsx b/features/withdrawals/request/wallet/wallet.tsx index f94442505..fb3c7ddea 100644 --- a/features/withdrawals/request/wallet/wallet.tsx +++ b/features/withdrawals/request/wallet/wallet.tsx @@ -1,6 +1,6 @@ import { memo } from 'react'; import { Divider } from '@lidofinance/lido-ui'; -import { useWeb3 } from '@reef-knot/web3-react'; +import { useWeb3 } from 'reef-knot/web3-react'; import { useSDK } from '@lido-sdk/react'; import { CardAccount, CardRow, Fallback } from 'shared/wallet'; diff --git a/features/withdrawals/shared/input-decorator-tvl-stake/input-decorator-tvl-stake.tsx b/features/withdrawals/shared/input-decorator-tvl-stake/input-decorator-tvl-stake.tsx index e7aa5e6e0..36456550a 100644 --- a/features/withdrawals/shared/input-decorator-tvl-stake/input-decorator-tvl-stake.tsx +++ b/features/withdrawals/shared/input-decorator-tvl-stake/input-decorator-tvl-stake.tsx @@ -18,7 +18,7 @@ export const InputDecoratorTvlStake = ({ size="xxs" variant="translucent" data-testid="letsStakeBtn" - onClick={() => push(`/${queryString}`)} + onClick={() => void push(`/${queryString}`)} > Yes, let`s stake diff --git a/features/withdrawals/shared/tx-stage-modal/stages/icons.tsx b/features/withdrawals/shared/tx-stage-modal/stages/icons.tsx index 00e37447f..1fe1511da 100644 --- a/features/withdrawals/shared/tx-stage-modal/stages/icons.tsx +++ b/features/withdrawals/shared/tx-stage-modal/stages/icons.tsx @@ -22,6 +22,11 @@ export const iconsDict = { ), + [TX_STAGE.SUCCESS_MULTISIG]: ( + + + + ), [TX_STAGE.SIGN]: ( @@ -54,6 +59,11 @@ export const iconsDict = { ), + [TX_STAGE.SUCCESS_MULTISIG]: ( + + + + ), [TX_STAGE.FAIL]: ( diff --git a/features/withdrawals/shared/tx-stage-modal/stages/index.ts b/features/withdrawals/shared/tx-stage-modal/stages/index.ts index cc4409aee..ae1b08ec6 100644 --- a/features/withdrawals/shared/tx-stage-modal/stages/index.ts +++ b/features/withdrawals/shared/tx-stage-modal/stages/index.ts @@ -1,5 +1,6 @@ export * from './tx-stage-pending'; export * from './tx-stage-success'; +export * from './tx-stage-success-multisig'; export * from './tx-stage-sign'; export * from './tx-stage-permit'; export * from './tx-stage-fail'; diff --git a/features/withdrawals/shared/tx-stage-modal/stages/tx-stage-success-multisig.tsx b/features/withdrawals/shared/tx-stage-modal/stages/tx-stage-success-multisig.tsx new file mode 100644 index 000000000..1b918294b --- /dev/null +++ b/features/withdrawals/shared/tx-stage-modal/stages/tx-stage-success-multisig.tsx @@ -0,0 +1,16 @@ +import { useConnectorInfo } from 'reef-knot/web3-react'; + +import { TxStageModalContent } from 'shared/components/tx-stage-modal-content'; +import { getStageIcon } from './icons'; +import { TX_STAGE } from '../types'; + +export const TxStageSuccessMultisig = () => { + const { isLedger } = useConnectorInfo(); + return ( + + ); +}; diff --git a/features/withdrawals/shared/tx-stage-modal/tx-stage-modal.tsx b/features/withdrawals/shared/tx-stage-modal/tx-stage-modal.tsx index cb2ac7352..5dec3e675 100644 --- a/features/withdrawals/shared/tx-stage-modal/tx-stage-modal.tsx +++ b/features/withdrawals/shared/tx-stage-modal/tx-stage-modal.tsx @@ -17,7 +17,9 @@ export const TxStageModal: FC = (props) => { () => isLedger && txStage && - ![TX_STAGE.SUCCESS, TX_STAGE.FAIL].includes(txStage), + ![TX_STAGE.SUCCESS, TX_STAGE.SUCCESS_MULTISIG, TX_STAGE.FAIL].includes( + txStage, + ), [isLedger, txStage], ); diff --git a/features/withdrawals/shared/tx-stage-modal/types.ts b/features/withdrawals/shared/tx-stage-modal/types.ts index 3b32dc266..58afa2173 100644 --- a/features/withdrawals/shared/tx-stage-modal/types.ts +++ b/features/withdrawals/shared/tx-stage-modal/types.ts @@ -5,6 +5,7 @@ export enum TX_STAGE { SIGN, BLOCK, SUCCESS, + SUCCESS_MULTISIG, FAIL, BUNKER, } diff --git a/features/wrap/features/unwrap-form/hooks.ts b/features/wrap/features/unwrap-form/hooks.ts index 3667d2cfa..92196235f 100644 --- a/features/wrap/features/unwrap-form/hooks.ts +++ b/features/wrap/features/unwrap-form/hooks.ts @@ -1,9 +1,10 @@ import { parseEther } from '@ethersproject/units'; -import { getStaticRpcBatchProvider } from 'utils/rpcProviders'; import { useLidoSWR, useWSTETHContractRPC } from '@lido-sdk/react'; import { useWeb3 } from 'reef-knot/web3-react'; -import { ESTIMATE_ACCOUNT, getBackendRPCPath, UNWRAP_GAS_LIMIT } from 'config'; +import { ESTIMATE_ACCOUNT, UNWRAP_GAS_LIMIT } from 'config'; import { BigNumber } from 'ethers'; +import { getFeeData } from 'utils/getFeeData'; +import { CHAINS } from '@lido-sdk/constants'; export const useUnwrapGasLimit = () => { const wsteth = useWSTETHContractRPC(); @@ -16,13 +17,7 @@ export const useUnwrapGasLimit = () => { return; } - const provider = getStaticRpcBatchProvider( - // TODO: add a way to type useWeb3 hook - chainId as number, - getBackendRPCPath(chainId as number), - ); - - const feeData = await provider.getFeeData(); + const feeData = await getFeeData(chainId as CHAINS); const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; const maxFeePerGas = feeData.maxFeePerGas ?? undefined; diff --git a/features/wrap/features/unwrap-form/unwrap-form.tsx b/features/wrap/features/unwrap-form/unwrap-form.tsx index 8b2c9a1dc..f1fe58826 100644 --- a/features/wrap/features/unwrap-form/unwrap-form.tsx +++ b/features/wrap/features/unwrap-form/unwrap-form.tsx @@ -78,7 +78,6 @@ export const UnwrapForm: FC = memo(() => { providerWeb3, wstethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -97,7 +96,6 @@ export const UnwrapForm: FC = memo(() => { providerWeb3, wstethContractWeb3, openTxModal, - closeTxModal, wstethBalance.update, stethBalance.update, chainId, diff --git a/features/wrap/features/wrap-form/form.tsx b/features/wrap/features/wrap-form/form.tsx index 731e145e9..a3a0f6e7d 100644 --- a/features/wrap/features/wrap-form/form.tsx +++ b/features/wrap/features/wrap-form/form.tsx @@ -14,7 +14,7 @@ import { useWSTETHContractWeb3, useSDK, } from '@lido-sdk/react'; -import { useWeb3 } from '@reef-knot/web3-react'; +import { useWeb3 } from 'reef-knot/web3-react'; import { useCurrencyInput } from 'shared/forms/hooks/useCurrencyInput'; import { useIsMultisig } from 'shared/hooks/useIsMultisig'; import { wrapProcessingWithApprove } from 'features/wrap/utils'; @@ -49,7 +49,6 @@ type FromProps = { setTxOperation: (value: TX_OPERATION) => void; setInputValue: (value: string) => void; openTxModal: () => void; - closeTxModal: () => void; setTxStage: (value: TX_STAGE) => void; setTxHash: (value?: string) => void; setTxModalFailedText: (value: string) => void; @@ -67,7 +66,6 @@ export const Form: FC = (props) => { setWrappingAmountValue, setTxOperation, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -108,7 +106,6 @@ export const Form: FC = (props) => { providerWeb3, wstethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -134,7 +131,6 @@ export const Form: FC = (props) => { chainId, wstethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, diff --git a/features/wrap/features/wrap-form/hooks.tsx b/features/wrap/features/wrap-form/hooks.tsx index bcb02468c..2a18b3cf0 100644 --- a/features/wrap/features/wrap-form/hooks.tsx +++ b/features/wrap/features/wrap-form/hooks.tsx @@ -1,5 +1,5 @@ import { parseEther } from '@ethersproject/units'; -import { CHAINS } from '@lido-sdk/constants'; + import { getStaticRpcBatchProvider } from '@lido-sdk/providers'; import { useLidoSWR, @@ -17,6 +17,7 @@ import { } from 'config'; import { BigNumber } from 'ethers'; import { STRATEGY_IMMUTABLE } from 'utils/swrStrategies'; +import { CHAINS } from '@lido-sdk/constants'; export const useApproveGasLimit = () => { const steth = useSTETHContractRPC(); @@ -63,18 +64,12 @@ export const useWrapGasLimit = (fromEther: boolean) => { getBackendRPCPath(chainId as CHAINS), ); - const feeData = await provider.getFeeData(); - const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined; - const maxFeePerGas = feeData.maxFeePerGas ?? undefined; - if (fromEther) { const gasLimit = await provider .estimateGas({ from: ESTIMATE_ACCOUNT, to: wsteth.address, value: parseEther('0.001'), - maxPriorityFeePerGas, - maxFeePerGas, }) .catch((error) => { console.warn(error); @@ -86,8 +81,6 @@ export const useWrapGasLimit = (fromEther: boolean) => { const gasLimit = await wsteth.estimateGas .wrap(parseEther('0.0001'), { from: ESTIMATE_ACCOUNT, - maxPriorityFeePerGas, - maxFeePerGas, }) .catch((error) => { console.warn(error); diff --git a/features/wrap/features/wrap-form/wrap-form.tsx b/features/wrap/features/wrap-form/wrap-form.tsx index add85b2e2..9181d3b63 100644 --- a/features/wrap/features/wrap-form/wrap-form.tsx +++ b/features/wrap/features/wrap-form/wrap-form.tsx @@ -104,8 +104,8 @@ export const WrapForm: FC = memo(() => { ); if (isMultisig) { - setTxStage(TX_STAGE.IDLE); - closeTxModal(); + setTxStage(TX_STAGE.SUCCESS_MULTISIG); + openTxModal(); return; } @@ -130,7 +130,7 @@ export const WrapForm: FC = memo(() => { openTxModal(); } }, - [openTxModal, closeTxModal, isMultisig], + [openTxModal, isMultisig], ); const { @@ -167,7 +167,6 @@ export const WrapForm: FC = memo(() => { setTxOperation={setTxOperation} setInputValue={setInputValue} openTxModal={openTxModal} - closeTxModal={closeTxModal} setTxStage={setTxStage} setTxHash={setTxHash} setTxModalFailedText={setTxModalFailedText} diff --git a/features/wrap/utils.ts b/features/wrap/utils.ts index c30fa32f4..422dd9a3c 100644 --- a/features/wrap/utils.ts +++ b/features/wrap/utils.ts @@ -1,10 +1,10 @@ import { parseEther } from '@ethersproject/units'; +import { BigNumber } from 'ethers'; import { WstethAbi } from '@lido-sdk/contracts'; -import { getTokenAddress, TOKENS } from '@lido-sdk/constants'; +import { CHAINS, getTokenAddress, TOKENS } from '@lido-sdk/constants'; import { TX_STAGE } from 'shared/components'; import { getErrorMessage, runWithTransactionLogger } from 'utils'; -import { getStaticRpcBatchProvider } from 'utils/rpcProviders'; -import { getBackendRPCPath } from 'config'; +import { getFeeData } from 'utils/getFeeData'; import invariant from 'tiny-invariant'; import type { Web3Provider } from '@ethersproject/providers'; @@ -14,12 +14,11 @@ type UnwrapProcessingProps = ( providerWeb3: Web3Provider | undefined, stethContractWeb3: WstethAbi | null, openTxModal: () => void, - closeTxModal: () => void, setTxStage: (value: TX_STAGE) => void, setTxHash: (value: string | undefined) => void, setTxModalFailedText: (value: string) => void, - wstethBalanceUpdate: () => void, - stethBalanceUpdate: () => void, + wstethBalanceUpdate: () => Promise, + stethBalanceUpdate: () => Promise, chainId: string | number | undefined, inputValue: string, resetForm: () => void, @@ -30,7 +29,6 @@ export const unwrapProcessing: UnwrapProcessingProps = async ( providerWeb3, wstethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -41,10 +39,8 @@ export const unwrapProcessing: UnwrapProcessingProps = async ( resetForm, isMultisig, ) => { - if (!wstethContractWeb3 || !chainId) { - return; - } - + invariant(wstethContractWeb3, 'must have wstethContractWeb3'); + invariant(chainId, 'must have chain id'); invariant(providerWeb3, 'must have providerWeb3'); try { @@ -55,14 +51,12 @@ export const unwrapProcessing: UnwrapProcessingProps = async ( ); return providerWeb3.getSigner().sendUncheckedTransaction(tx); } else { - const provider = getStaticRpcBatchProvider( - chainId, - getBackendRPCPath(chainId), + const { maxFeePerGas, maxPriorityFeePerGas } = await getFeeData( + chainId as CHAINS, ); - const feeData = await provider.getFeeData(); return wstethContractWeb3.unwrap(parseEther(inputValue), { - maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, - maxFeePerGas: feeData.maxFeePerGas ?? undefined, + maxPriorityFeePerGas: maxPriorityFeePerGas ?? undefined, + maxFeePerGas: maxFeePerGas ?? undefined, }); } }; @@ -76,14 +70,15 @@ export const unwrapProcessing: UnwrapProcessingProps = async ( ); const handleEnding = () => { + openTxModal(); resetForm(); - stethBalanceUpdate(); - wstethBalanceUpdate(); + void stethBalanceUpdate(); + void wstethBalanceUpdate(); }; if (isMultisig) { + setTxStage(TX_STAGE.SUCCESS_MULTISIG); handleEnding(); - closeTxModal(); return; } @@ -96,9 +91,8 @@ export const unwrapProcessing: UnwrapProcessingProps = async ( ); } - handleEnding(); setTxStage(TX_STAGE.SUCCESS); - openTxModal(); + handleEnding(); /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ } catch (error: any) { // errors are sometimes nested :( @@ -114,17 +108,16 @@ type WrapProcessingWithApproveProps = ( providerWeb3: Web3Provider | undefined, stethContractWeb3: WstethAbi | null, openTxModal: () => void, - closeTxModal: () => void, setTxStage: (value: TX_STAGE) => void, setTxHash: (value: string | undefined) => void, setTxModalFailedText: (value: string) => void, - ethBalanceUpdate: () => void, - stethBalanceUpdate: () => void, + ethBalanceUpdate: () => Promise, + stethBalanceUpdate: () => Promise, inputValue: string, selectedToken: string, needsApprove: boolean, isMultisig: boolean, - approve: () => void, + approve: () => Promise, resetForm: () => void, ) => Promise; @@ -133,7 +126,6 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( providerWeb3, wstethContractWeb3, openTxModal, - closeTxModal, setTxStage, setTxHash, setTxModalFailedText, @@ -146,26 +138,15 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( approve, resetForm, ) => { - if (!chainId || !wstethContractWeb3) { - return; - } - - invariant(providerWeb3, 'must have providerWeb3'); - - const wstethTokenAddress = getTokenAddress(chainId, TOKENS.WSTETH); - const handleEnding = () => { + openTxModal(); resetForm(); - ethBalanceUpdate(); - stethBalanceUpdate(); + void ethBalanceUpdate(); + void stethBalanceUpdate(); }; const getGasParameters = async () => { - const provider = getStaticRpcBatchProvider( - chainId, - getBackendRPCPath(chainId), - ); - const feeData = await provider.getFeeData(); + const feeData = await getFeeData(chainId as CHAINS); return { maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, maxFeePerGas: feeData.maxFeePerGas ?? undefined, @@ -173,6 +154,12 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( }; try { + invariant(providerWeb3, 'must have providerWeb3'); + invariant(wstethContractWeb3, 'must have wstethContractWeb3'); + invariant(chainId, 'must have chain id'); + + const wstethTokenAddress = getTokenAddress(chainId, TOKENS.WSTETH); + if (selectedToken === ETH) { const callback = async () => { if (isMultisig) { @@ -198,7 +185,7 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( ); if (isMultisig) { - closeTxModal(); + setTxStage(TX_STAGE.SUCCESS_MULTISIG); handleEnding(); return; } @@ -212,12 +199,11 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( ); } - handleEnding(); setTxStage(TX_STAGE.SUCCESS); - openTxModal(); + handleEnding(); } else if (selectedToken === TOKENS.STETH) { if (needsApprove) { - approve(); + return approve(); } else { const callback = async () => { if (isMultisig) { @@ -242,7 +228,7 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( ); if (isMultisig) { - closeTxModal(); + setTxStage(TX_STAGE.SUCCESS_MULTISIG); handleEnding(); return; } @@ -256,9 +242,8 @@ export const wrapProcessingWithApprove: WrapProcessingWithApproveProps = async ( ); } - handleEnding(); setTxStage(TX_STAGE.SUCCESS); - openTxModal(); + handleEnding(); } } /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ diff --git a/next.config.mjs b/next.config.mjs index 9d8481d99..a6a7be78b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -59,17 +59,17 @@ export default withBundleAnalyzer({ esmExternals: true, }, webpack(config) { - // Teach webpack to import svg files - config.module.rules.push({ - test: /\.svg$/, - use: ['@svgr/webpack', 'url-loader'], - }); - - // Teach webpack to import md files - config.module.rules.push({ - test: /\.md$/, - use: 'raw-loader', - }); + // Teach webpack to import svg and md files + config.module.rules.push( + { + test: /\.svg$/, + use: ['@svgr/webpack', 'url-loader'], + }, + { + test: /\.md$/, + use: 'raw-loader', + }, + ); return config; }, diff --git a/package.json b/package.json index 8d9e76211..aad760101 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,9 @@ "@commitlint/cli": "^17.4.4", "@commitlint/config-conventional": "^17.4.4", "@commitlint/prompt": "^17.4.4", + "@lidofinance/eslint-config": "^0.34.0", "@next/bundle-analyzer": "^13.2.4", + "@next/eslint-plugin-next": "^13.4.13", "@playwright/test": "^1.29.2", "@svgr/webpack": "^8.0.1", "@typechain/ethers-v5": "^7.0.1", @@ -96,21 +98,26 @@ "@types/styled-components": "^5.1.11", "@types/styled-system": "5.1.15", "@types/winston": "^2.4.4", - "@typescript-eslint/eslint-plugin": "^5.12.0", - "@typescript-eslint/parser": "^5.12.0", - "eslint": "8.9.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-jsx-a11y": "^6.4.1", - "eslint-plugin-prettier": "^3.4.0", - "eslint-plugin-react": "^7.24.0", - "eslint-plugin-react-hooks": "^4.3.0", + "@typescript-eslint/eslint-plugin": "^6.2.1", + "@typescript-eslint/parser": "^6.2.1", + "eslint": "^8.46.0", + "eslint-config-prettier": "^9.0.0", + "eslint-import-resolver-typescript": "^3.5.5", + "eslint-plugin-eslint-comments": "^3.2.0", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-jest": "^27.2.3", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.33.1", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-sonarjs": "^0.20.0", + "eslint-plugin-unicorn": "^48.0.1", "husky": "^7.0.1", "jest": "^29.5.0", "jsonschema": "^1.4.1", "lint-staged": "^13.2.3", "playwright": "^1.29.2", - "prettier": "^2.3.2", + "prettier": "^3.0.1", "ts-jest": "^29.1.0", "typechain": "^5.1.2", "typescript": "^4.9.4", diff --git a/pages/_document.tsx b/pages/_document.tsx index 18d0e5820..871269d67 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -107,6 +107,7 @@ export default class MyDocument extends Document { + {/* eslint-disable-next-line @next/next/no-sync-scripts */}