From 9d4fa3d41a7e736c3164876cfc2d06765d251cef Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Sun, 14 Jul 2024 11:59:30 +0300 Subject: [PATCH 01/40] refactor: remove 'active' from 'useWeb3' --- .../use-version-check.ts | 8 +++---- .../stake-form-context/validation.ts | 12 +++++----- .../claim-form-context/claim-form-context.tsx | 23 ++++++++++--------- .../form/requests-list/requests-list.tsx | 13 ++++++----- .../request/form/transaction-info.tsx | 12 ++++++---- .../use-validation-context.ts | 8 +++---- .../use-unwra-form-validation-context.ts | 10 ++++---- .../hooks/use-wrap-form-validation-context.ts | 10 ++++---- features/wsteth/wrap/wrap-form/wrap-stats.tsx | 6 ++--- .../form-controller/form-controller.tsx | 10 ++++---- shared/hooks/use-staking-limit-warning.ts | 8 +++---- .../hooks/use-transaction-modal-stage.tsx | 8 +++---- 12 files changed, 67 insertions(+), 61 deletions(-) diff --git a/features/ipfs/security-status-banner/use-version-check.ts b/features/ipfs/security-status-banner/use-version-check.ts index 414a24e37..f74449dbf 100644 --- a/features/ipfs/security-status-banner/use-version-check.ts +++ b/features/ipfs/security-status-banner/use-version-check.ts @@ -1,12 +1,12 @@ import { useEffect, useState } from 'react'; -import { useLidoSWR } from '@lido-sdk/react'; -import { useWeb3 } from 'reef-knot/web3-react'; import { useForceDisconnect } from 'reef-knot/core-react'; +import { useLidoSWR } from '@lido-sdk/react'; import buildInfo from 'build-info.json'; import { config } from 'config'; import { useUserConfig } from 'config/user-config'; import { STRATEGY_IMMUTABLE } from 'consts/swr-strategies'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { overrideWithQAMockBoolean } from 'utils/qa'; import { isVersionLess } from './utils'; @@ -19,7 +19,7 @@ const URL_CID_REGEX = /[/.](?Qm[1-9A-HJ-NP-Za-km-z]{44,}|b[A-Za-z2-7]{58,}|B[A-Z2-7]{58,}|z[1-9A-HJ-NP-Za-km-z]{48,}|F[0-9A-F]{50,})([./#?]|$)/; export const useVersionCheck = () => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { setIsWalletConnectionAllowed } = useUserConfig(); const { forceDisconnect } = useForceDisconnect(); const [areConditionsAccepted, setConditionsAccepted] = useState(false); @@ -84,7 +84,7 @@ export const useVersionCheck = () => { forceDisconnect(); } }, [ - active, + isDappActive, forceDisconnect, isNotVerifiable, isVersionUnsafe, diff --git a/features/stake/stake-form/stake-form-context/validation.ts b/features/stake/stake-form/stake-form-context/validation.ts index 26d0cadcf..b908e6f9f 100644 --- a/features/stake/stake-form/stake-form-context/validation.ts +++ b/features/stake/stake-form/stake-form-context/validation.ts @@ -1,9 +1,9 @@ import { useMemo } from 'react'; import invariant from 'tiny-invariant'; -import { useWeb3 } from 'reef-knot/web3-react'; import { Zero } from '@ethersproject/constants'; import { validateEtherAmount } from 'shared/hook-form/validation/validate-ether-amount'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { VALIDATION_CONTEXT_TIMEOUT } from 'features/withdrawals/withdrawals-constants'; import { handleResolverValidationError } from 'shared/hook-form/validation/validation-error'; import { awaitWithTimeout } from 'utils/await-with-timeout'; @@ -72,26 +72,26 @@ export const stakeFormValidationResolver: Resolver< export const useStakeFormValidationContext = ( networkData: StakeFormNetworkData, ): Promise => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { stakingLimitInfo, etherBalance, isMultisig, gasCost } = networkData; const validationContextAwaited = useMemo(() => { if ( stakingLimitInfo && // we ether not connected or must have all account related data - (!active || (etherBalance && gasCost && isMultisig !== undefined)) + (!isDappActive || (etherBalance && gasCost && isMultisig !== undefined)) ) { return { - isWalletActive: active, + isWalletActive: isDappActive, stakingLimitLevel: stakingLimitInfo.stakeLimitLevel, currentStakeLimit: stakingLimitInfo.currentStakeLimit, - // condition above guaranties stubs will only be passed when active = false + // condition above guaranties stubs will only be passed when isDappActive = false etherBalance: etherBalance ?? Zero, gasCost: gasCost ?? Zero, isMultisig: isMultisig ?? false, }; } return undefined; - }, [active, etherBalance, gasCost, isMultisig, stakingLimitInfo]); + }, [isDappActive, etherBalance, gasCost, isMultisig, stakingLimitInfo]); return useAwaiter(validationContextAwaited).awaiter; }; diff --git a/features/withdrawals/claim/claim-form-context/claim-form-context.tsx b/features/withdrawals/claim/claim-form-context/claim-form-context.tsx index ac4c471b6..0c50cec66 100644 --- a/features/withdrawals/claim/claim-form-context/claim-form-context.tsx +++ b/features/withdrawals/claim/claim-form-context/claim-form-context.tsx @@ -7,24 +7,25 @@ import { useMemo, } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; - import invariant from 'tiny-invariant'; + +import { useClaim } from 'features/withdrawals/hooks'; +import { useClaimData } from 'features/withdrawals/contexts/claim-data-context'; +import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; +import { + FormControllerContext, + FormControllerContextValueType, +} from 'shared/hook-form/form-controller'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; + import { ClaimFormInputType, ClaimFormValidationContext } from './types'; import { claimFormValidationResolver } from './validation'; -import { useClaim } from 'features/withdrawals/hooks'; import { useMaxSelectedCount } from './use-max-selected-count'; -import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; import { generateDefaultValues, useGetDefaultValues, } from './use-default-values'; import { ClaimFormHelperState, useHelperState } from './use-helper-state'; -import { - FormControllerContext, - FormControllerContextValueType, -} from 'shared/hook-form/form-controller'; -import { useClaimData } from 'features/withdrawals/contexts/claim-data-context'; -import { useWeb3 } from 'reef-knot/web3-react'; type ClaimFormDataContextValueType = ClaimFormHelperState; @@ -39,7 +40,7 @@ export const useClaimFormData = () => { }; export const ClaimFormProvider: FC = ({ children }) => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { data } = useClaimData(); const { maxSelectedRequestCount, defaultSelectedRequestCount } = @@ -49,7 +50,7 @@ export const ClaimFormProvider: FC = ({ children }) => { const formObject = useForm({ defaultValues: getDefaultValues, resolver: claimFormValidationResolver, - context: { maxSelectedRequestCount, isWalletActive: active }, + context: { maxSelectedRequestCount, isWalletActive: isDappActive }, mode: 'onChange', reValidateMode: 'onChange', }); diff --git a/features/withdrawals/claim/form/requests-list/requests-list.tsx b/features/withdrawals/claim/form/requests-list/requests-list.tsx index 47ace4b23..415b05bae 100644 --- a/features/withdrawals/claim/form/requests-list/requests-list.tsx +++ b/features/withdrawals/claim/form/requests-list/requests-list.tsx @@ -1,13 +1,14 @@ +import { useFieldArray, useFormContext, useFormState } from 'react-hook-form'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; + +import { ClaimFormInputType } from '../../claim-form-context'; import { RequestItem } from './request-item'; import { RequestsEmpty } from './requests-empty'; import { Wrapper } from './styles'; import { RequestsLoader } from './requests-loader'; -import { useFieldArray, useFormContext, useFormState } from 'react-hook-form'; -import { ClaimFormInputType } from '../../claim-form-context'; -import { useWeb3 } from 'reef-knot/web3-react'; export const RequestsList: React.FC = () => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { isLoading } = useFormState(); const { register } = useFormContext(); const { fields } = useFieldArray({ @@ -18,8 +19,8 @@ export const RequestsList: React.FC = () => { return ; } - if (!active || fields.length === 0) { - return ; + if (!isDappActive || fields.length === 0) { + return ; } return ( diff --git a/features/withdrawals/request/form/transaction-info.tsx b/features/withdrawals/request/form/transaction-info.tsx index 866b466b8..d894fbf80 100644 --- a/features/withdrawals/request/form/transaction-info.tsx +++ b/features/withdrawals/request/form/transaction-info.tsx @@ -1,21 +1,23 @@ +import { useWatch } from 'react-hook-form'; import { TOKENS } from '@lido-sdk/constants'; import { DataTableRow } from '@lidofinance/lido-ui'; + import { useRequestTxPrice } from 'features/withdrawals/hooks/useWithdrawTxPrice'; import { useApproveGasLimit } from 'features/wsteth/wrap/hooks/use-approve-gas-limit'; -import { useWatch } from 'react-hook-form'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { AllowanceDataTableRow } from 'shared/components/allowance-data-table-row'; import { DataTableRowStethByWsteth } from 'shared/components/data-table-row-steth-by-wsteth'; import { FormatPrice } from 'shared/formatters'; import { useTxCostInUsd } from 'shared/hooks'; + import { RequestFormInputType, useRequestFormData, useValidationResults, } from '../request-form-context'; -import { useWeb3 } from 'reef-knot/web3-react'; -import { AllowanceDataTableRow } from 'shared/components/allowance-data-table-row'; export const TransactionInfo = () => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { isApprovalFlow, isApprovalFlowLoading, allowance } = useRequestFormData(); const token = useWatch({ name: 'token' }); @@ -55,7 +57,7 @@ export const TransactionInfo = () => { data-testid="allowance" token={token} allowance={allowance} - isBlank={!active} + isBlank={!isDappActive} loading={isApprovalFlowLoading} /> {token === TOKENS.STETH ? ( diff --git a/features/withdrawals/request/request-form-context/use-validation-context.ts b/features/withdrawals/request/request-form-context/use-validation-context.ts index af171e763..904523adc 100644 --- a/features/withdrawals/request/request-form-context/use-validation-context.ts +++ b/features/withdrawals/request/request-form-context/use-validation-context.ts @@ -1,23 +1,23 @@ +import { useMemo } from 'react'; import { MAX_REQUESTS_COUNT_LEDGER_LIMIT, MAX_REQUESTS_COUNT, } from 'features/withdrawals/withdrawals-constants'; -import { useMemo } from 'react'; import { useIsLedgerLive } from 'shared/hooks/useIsLedgerLive'; import { useAwaiter } from 'shared/hooks/use-awaiter'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import type { RequestFormDataType, RequestFormValidationAsyncContextType, RequestFormValidationContextType, } from './types'; -import { useWeb3 } from 'reef-knot/web3-react'; // Prepares validation context object from request form data export const useValidationContext = ( requestData: RequestFormDataType, setIntermediateValidationResults: RequestFormValidationContextType['setIntermediateValidationResults'], ): RequestFormValidationContextType => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const isLedgerLive = useIsLedgerLive(); const maxRequestCount = isLedgerLive ? MAX_REQUESTS_COUNT_LEDGER_LIMIT @@ -68,7 +68,7 @@ export const useValidationContext = ( useAwaiter(context).awaiter; return { - isWalletActive: active, + isWalletActive: isDappActive, asyncContext, setIntermediateValidationResults, }; diff --git a/features/wsteth/unwrap/hooks/use-unwra-form-validation-context.ts b/features/wsteth/unwrap/hooks/use-unwra-form-validation-context.ts index 92d4f0783..22e96dfa0 100644 --- a/features/wsteth/unwrap/hooks/use-unwra-form-validation-context.ts +++ b/features/wsteth/unwrap/hooks/use-unwra-form-validation-context.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { useAwaiter } from 'shared/hooks/use-awaiter'; import type { UnwrapFormNetworkData } from '../unwrap-form-context'; @@ -11,18 +11,18 @@ type UseUnwrapFormValidationContextArgs = { export const useUnwrapFormValidationContext = ({ networkData, }: UseUnwrapFormValidationContextArgs) => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { maxAmount } = networkData; const validationContextAwaited = useMemo(() => { - if (active && !maxAmount) { + if (isDappActive && !maxAmount) { return undefined; } return { - isWalletActive: active, + isWalletActive: isDappActive, maxAmount, }; - }, [active, maxAmount]); + }, [isDappActive, maxAmount]); return useAwaiter(validationContextAwaited).awaiter; }; diff --git a/features/wsteth/wrap/hooks/use-wrap-form-validation-context.ts b/features/wsteth/wrap/hooks/use-wrap-form-validation-context.ts index 6ad2763f3..e044da344 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-validation-context.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-validation-context.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { useAwaiter } from 'shared/hooks/use-awaiter'; import type { @@ -15,7 +15,7 @@ type UseWrapFormValidationContextArgs = { export const useWrapFormValidationContext = ({ networkData, }: UseWrapFormValidationContextArgs): WrapFormValidationContext => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { stakeLimitInfo, ethBalance, @@ -24,7 +24,7 @@ export const useWrapFormValidationContext = ({ wrapEthGasCost, } = networkData; - const waitForAccountData = active + const waitForAccountData = isDappActive ? stethBalance && ethBalance && isMultisig !== undefined : true; @@ -38,7 +38,7 @@ export const useWrapFormValidationContext = ({ useMemo(() => { return isDataReady ? ({ - isWalletActive: active, + isWalletActive: isDappActive, stethBalance, etherBalance: ethBalance, isMultisig, @@ -49,7 +49,7 @@ export const useWrapFormValidationContext = ({ : undefined; }, [ isDataReady, - active, + isDappActive, stethBalance, ethBalance, isMultisig, diff --git a/features/wsteth/wrap/wrap-form/wrap-stats.tsx b/features/wsteth/wrap/wrap-form/wrap-stats.tsx index 4767eb814..1d26d5e2c 100644 --- a/features/wsteth/wrap/wrap-form/wrap-stats.tsx +++ b/features/wsteth/wrap/wrap-form/wrap-stats.tsx @@ -1,5 +1,4 @@ import { useFormContext } from 'react-hook-form'; -import { useWeb3 } from 'reef-knot/web3-react'; import { parseEther } from '@ethersproject/units'; import { DataTable, DataTableRow } from '@lidofinance/lido-ui'; @@ -11,6 +10,7 @@ import { TOKENS_TO_WRAP } from 'features/wsteth/shared/types'; import { AllowanceDataTableRow } from 'shared/components/allowance-data-table-row'; import { FormatPrice, FormatToken } from 'shared/formatters'; import { useTxCostInUsd, useWstethBySteth } from 'shared/hooks'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { useApproveGasLimit } from '../hooks/use-approve-gas-limit'; import { useWrapFormData, WrapFormInputType } from '../wrap-form-context'; @@ -18,7 +18,7 @@ import { useWrapFormData, WrapFormInputType } from '../wrap-form-context'; const oneSteth = parseEther('1'); export const WrapFormStats = () => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { allowance, wrapGasLimit, isApprovalLoading } = useWrapFormData(); const { watch } = useFormContext(); @@ -82,7 +82,7 @@ export const WrapFormStats = () => { diff --git a/shared/hook-form/form-controller/form-controller.tsx b/shared/hook-form/form-controller/form-controller.tsx index e06b24f60..b1a799249 100644 --- a/shared/hook-form/form-controller/form-controller.tsx +++ b/shared/hook-form/form-controller/form-controller.tsx @@ -1,6 +1,8 @@ import { FC, PropsWithChildren, useEffect, useMemo } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; import { useFormContext } from 'react-hook-form'; + +import { useDappStatus } from 'shared/hooks/use-dapp-status'; + import { useFormControllerContext } from './form-controller-context'; type FormControllerProps = React.ComponentProps<'form'>; @@ -9,7 +11,7 @@ export const FormController: FC> = ({ children, ...props }) => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { handleSubmit, reset: resetDefault } = useFormContext(); const { onSubmit, @@ -34,11 +36,11 @@ export const FormController: FC> = ({ // Reset form amount after disconnect wallet useEffect(() => { - if (!active) resetDefault(); + if (!isDappActive) resetDefault(); // reset will be captured when active changes // so we don't need it in deps // eslint-disable-next-line react-hooks/exhaustive-deps - }, [active]); + }, [isDappActive]); return (
diff --git a/shared/hooks/use-staking-limit-warning.ts b/shared/hooks/use-staking-limit-warning.ts index 35f07abb5..f0f8d59b9 100644 --- a/shared/hooks/use-staking-limit-warning.ts +++ b/shared/hooks/use-staking-limit-warning.ts @@ -1,15 +1,15 @@ -import { useWeb3 } from 'reef-knot/web3-react'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { LIMIT_LEVEL } from 'types'; export const useStakingLimitWarning = (stakingLimitLevel?: LIMIT_LEVEL) => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const limitWarning = - stakingLimitLevel === LIMIT_LEVEL.WARN && active + stakingLimitLevel === LIMIT_LEVEL.WARN && isDappActive ? 'Stake limit is almost exhausted. Your transaction may not go through.' : null; const limitError = - stakingLimitLevel === LIMIT_LEVEL.REACHED && active + stakingLimitLevel === LIMIT_LEVEL.REACHED && isDappActive ? 'Stake limit is exhausted. Please wait until the limit is restored.' : null; diff --git a/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx b/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx index 4403fb2ca..3a6f50da5 100644 --- a/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx +++ b/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; import { useModalActions } from 'providers/modal-provider'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { useTransactionModal, TransactionModal } from '../transaction-modal'; export type TransactionModalTransitStage = ( @@ -12,7 +12,7 @@ export type TransactionModalTransitStage = ( export const useTransactionModalStage = >( getStages: (transitStage: TransactionModalTransitStage) => S, ) => { - const { active } = useWeb3(); + const { isDappActive } = useDappStatus(); const { openModal } = useTransactionModal(); const { closeModal } = useModalActions(); const isMountedRef = useRef(true); @@ -39,10 +39,10 @@ export const useTransactionModalStage = >( }, []); useEffect(() => { - if (!active) { + if (!isDappActive) { closeModal(TransactionModal); } - }, [active, closeModal]); + }, [isDappActive, closeModal]); return { txModalStages, From 6f8b1f737d606e918635e31b2bc3addfdaa038af Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Sun, 14 Jul 2024 12:26:48 +0300 Subject: [PATCH 02/40] refactor: remove 'chainId' and 'account' from 'useWeb3' --- features/stake/stake-form/use-stake.ts | 12 +++---- .../claim/form/requests-list/request-item.tsx | 4 +-- .../withdrawals/hooks/contract/useClaim.ts | 21 ++++++------ .../withdrawals/hooks/contract/useRequest.ts | 34 +++++++++---------- .../hooks/contract/useWithdrawalsContract.ts | 6 ++-- .../hooks/contract/useWithdrawalsData.ts | 6 ++-- .../withdrawals/hooks/useNftDataByTxHash.ts | 8 ++--- .../withdrawals/hooks/useWithdrawTxPrice.ts | 12 +++---- .../hooks/use-unwrap-form-processing.ts | 19 ++++++----- .../wrap/hooks/use-approve-gas-limit.tsx | 4 +-- .../wrap/hooks/use-wrap-form-processing.ts | 19 ++++++----- .../wsteth/wrap/hooks/use-wrap-gas-limit.ts | 4 +-- .../wsteth/wrap/hooks/use-wrap-tx-approve.ts | 17 ++++++---- shared/hooks/useERC20PermitSignature.ts | 18 +++++----- shared/hooks/useIsMultisig.ts | 6 ++-- shared/wallet/wallet-modal/wallet-modal.tsx | 13 +++---- 16 files changed, 103 insertions(+), 100 deletions(-) diff --git a/features/stake/stake-form/use-stake.ts b/features/stake/stake-form/use-stake.ts index 997836c81..50895cc74 100644 --- a/features/stake/stake-form/use-stake.ts +++ b/features/stake/stake-form/use-stake.ts @@ -1,7 +1,7 @@ import { BigNumber } from 'ethers'; import { useCallback } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; import invariant from 'tiny-invariant'; +import { useAccount } from 'wagmi'; import { useSDK, useSTETHContractWeb3 } from '@lido-sdk/react'; @@ -31,7 +31,7 @@ type StakeOptions = { export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { const stethContractWeb3 = useSTETHContractWeb3(); - const { account, chainId } = useWeb3(); + const { address, chainId } = useAccount(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { providerWeb3, providerRpc } = useSDK(); const { txModalStages } = useTxModalStagesStake(); @@ -44,7 +44,7 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { try { invariant(amount, 'amount is null'); invariant(chainId, 'chainId is not defined'); - invariant(account, 'account is not defined'); + invariant(address, 'account is not defined'); invariant(providerWeb3, 'providerWeb3 not defined'); invariant(stethContractWeb3, 'steth is not defined'); @@ -58,7 +58,7 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { txModalStages.sign(amount); const [isMultisig, referralAddress] = await Promise.all([ - isContract(account, providerRpc), + isContract(address, providerRpc), referral ? getAddress(referral, providerRpc) : config.STAKE_FALLBACK_REFERRAL_ADDRESS, @@ -108,7 +108,7 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { ); } - const stethBalance = await stethContractWeb3.balanceOf(account); + const stethBalance = await stethContractWeb3.balanceOf(address); await onConfirm?.(); @@ -123,7 +123,7 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { }, [ chainId, - account, + address, providerWeb3, stethContractWeb3, txModalStages, diff --git a/features/withdrawals/claim/form/requests-list/request-item.tsx b/features/withdrawals/claim/form/requests-list/request-item.tsx index fcf25521d..39a20163a 100644 --- a/features/withdrawals/claim/form/requests-list/request-item.tsx +++ b/features/withdrawals/claim/form/requests-list/request-item.tsx @@ -1,6 +1,6 @@ import { forwardRef } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; import { useFormState, useWatch } from 'react-hook-form'; +import { useAccount } from 'wagmi'; import { Checkbox, CheckboxProps, External } from '@lidofinance/lido-ui'; import { FormatToken } from 'shared/formatters'; @@ -19,7 +19,7 @@ type RequestItemProps = { export const RequestItem = forwardRef( ({ token_id, name, disabled, index, ...props }, ref) => { - const { chainId } = useWeb3(); + const { chainId } = useAccount(); const { isSubmitting } = useFormState(); const { canSelectMore } = useClaimFormData(); const { checked, status } = useWatch< diff --git a/features/withdrawals/hooks/contract/useClaim.ts b/features/withdrawals/hooks/contract/useClaim.ts index 9cd1bcdf8..3aae1ef72 100644 --- a/features/withdrawals/hooks/contract/useClaim.ts +++ b/features/withdrawals/hooks/contract/useClaim.ts @@ -1,23 +1,24 @@ import { useCallback } from 'react'; import { BigNumber } from 'ethers'; +import invariant from 'tiny-invariant'; +import { useAccount } from 'wagmi'; + +import { useSDK } from '@lido-sdk/react'; import { useClaimData } from 'features/withdrawals/contexts/claim-data-context'; +import { RequestStatusClaimable } from 'features/withdrawals/types/request-status'; +import { useTxModalStagesClaim } from 'features/withdrawals/claim/transaction-modal-claim/use-tx-modal-stages-claim'; import { runWithTransactionLogger } from 'utils'; +import { isContract } from 'utils/isContract'; import { useWithdrawalsContract } from './useWithdrawalsContract'; -import { RequestStatusClaimable } from 'features/withdrawals/types/request-status'; -import invariant from 'tiny-invariant'; -import { isContract } from 'utils/isContract'; -import { useWeb3 } from 'reef-knot/web3-react'; -import { useSDK } from '@lido-sdk/react'; -import { useTxModalStagesClaim } from 'features/withdrawals/claim/transaction-modal-claim/use-tx-modal-stages-claim'; type Args = { onRetry?: () => void; }; export const useClaim = ({ onRetry }: Args) => { - const { account } = useWeb3(); + const { address } = useAccount(); const { providerWeb3 } = useSDK(); const { contractWeb3 } = useWithdrawalsContract(); const { optimisticClaimRequests } = useClaimData(); @@ -28,10 +29,10 @@ export const useClaim = ({ onRetry }: Args) => { try { invariant(contractWeb3, 'must have contract'); invariant(sortedRequests, 'must have requests'); - invariant(account, 'must have address'); + invariant(address, 'must have address'); invariant(providerWeb3, 'must have provider'); - const isMultisig = await isContract(account, contractWeb3.provider); + const isMultisig = await isContract(address, contractWeb3.provider); const amount = sortedRequests.reduce( (s, r) => s.add(r.claimableEth), @@ -98,7 +99,7 @@ export const useClaim = ({ onRetry }: Args) => { }, [ contractWeb3, - account, + address, providerWeb3, optimisticClaimRequests, txModalStages, diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index 45896bd3c..bc99bc6b1 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useWeb3 } from 'reef-knot/web3-react'; import { useAccount } from 'wagmi'; import { Zero } from '@ethersproject/constants'; import { @@ -32,7 +31,7 @@ import { useTransactionModal } from 'shared/transaction-modal/transaction-modal' const useWithdrawalRequestMethods = () => { const { providerWeb3 } = useSDK(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); - const { account, chainId, contractWeb3 } = useWithdrawalsContract(); + const { address, chainId, contractWeb3 } = useWithdrawalsContract(); const permitSteth = useCallback( async ({ @@ -43,7 +42,7 @@ const useWithdrawalRequestMethods = () => { requests: BigNumber[]; }) => { invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); + invariant(address, 'must have account'); invariant(signature, 'must have signature'); invariant(contractWeb3, 'must have contractWeb3'); @@ -78,7 +77,7 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider], + [address, chainId, contractWeb3, staticRpcProvider], ); const permitWsteth = useCallback( @@ -90,7 +89,7 @@ const useWithdrawalRequestMethods = () => { requests: BigNumber[]; }) => { invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); + invariant(address, 'must have account'); invariant(signature, 'must have signature'); invariant(contractWeb3, 'must have contractWeb3'); @@ -129,19 +128,19 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider], + [address, chainId, contractWeb3, staticRpcProvider], ); const steth = useCallback( async ({ requests }: { requests: BigNumber[] }) => { invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); + invariant(address, 'must have account'); invariant(contractWeb3, 'must have contractWeb3'); invariant(providerWeb3, 'must have providerWeb3'); - const isMultisig = await isContract(account, contractWeb3.provider); + const isMultisig = await isContract(address, contractWeb3.provider); - const params = [requests, account] as const; + const params = [requests, address] as const; const callback = async () => { if (isMultisig) { @@ -169,24 +168,24 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider, providerWeb3], + [address, chainId, contractWeb3, staticRpcProvider, providerWeb3], ); const wstETH = useCallback( async ({ requests }: { requests: BigNumber[] }) => { invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); + invariant(address, 'must have account'); invariant(contractWeb3, 'must have contractWeb3'); invariant(providerWeb3, 'must have providerWeb3'); - const isMultisig = await isContract(account, contractWeb3.provider); + const isMultisig = await isContract(address, contractWeb3.provider); - const params = [requests, account] as const; + const params = [requests, address] as const; const callback = async () => { if (isMultisig) { const tx = await contractWeb3.populateTransaction.requestWithdrawalsWstETH( requests, - account, + address, ); return providerWeb3?.getSigner().sendUncheckedTransaction(tx); } else { @@ -207,7 +206,7 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider, providerWeb3], + [address, chainId, contractWeb3, staticRpcProvider, providerWeb3], ); return useCallback( @@ -243,8 +242,7 @@ export const useWithdrawalRequest = ({ const { chainId } = useSDK(); const withdrawalQueueAddress = getWithdrawalQueueAddress(chainId); - const { connector } = useAccount(); - const { account } = useWeb3(); + const { connector, address } = useAccount(); const { isBunker } = useWithdrawals(); const { txModalStages } = useTxModalStagesRequest(); const getRequestMethod = useWithdrawalRequestMethods(); @@ -268,7 +266,7 @@ export const useWithdrawalRequest = ({ valueBN, tokenContract.address, withdrawalQueueAddress, - account ?? undefined, + address ?? undefined, ); const { gatherPermitSignature } = useERC20PermitSignature({ diff --git a/features/withdrawals/hooks/contract/useWithdrawalsContract.ts b/features/withdrawals/hooks/contract/useWithdrawalsContract.ts index e874c7b84..f19c23cdb 100644 --- a/features/withdrawals/hooks/contract/useWithdrawalsContract.ts +++ b/features/withdrawals/hooks/contract/useWithdrawalsContract.ts @@ -1,14 +1,14 @@ +import { useAccount } from 'wagmi'; import { useWithdrawalQueueContractWeb3, useWithdrawalQueueContractRPC, } from '@lido-sdk/react'; -import { useWeb3 } from 'reef-knot/web3-react'; export const useWithdrawalsContract = () => { const contractWeb3 = useWithdrawalQueueContractWeb3(); const contractRpc = useWithdrawalQueueContractRPC(); - const { account, chainId } = useWeb3(); + const { address, chainId } = useAccount(); - return { contractWeb3, contractRpc, account, chainId }; + return { contractWeb3, contractRpc, address, chainId }; }; diff --git a/features/withdrawals/hooks/contract/useWithdrawalsData.ts b/features/withdrawals/hooks/contract/useWithdrawalsData.ts index 592f97d8f..138078dec 100644 --- a/features/withdrawals/hooks/contract/useWithdrawalsData.ts +++ b/features/withdrawals/hooks/contract/useWithdrawalsData.ts @@ -80,15 +80,15 @@ const getRequestTimeForWQRequestIds = async ( }; export const useWithdrawalRequests = () => { - const { contractRpc, account, chainId } = useWithdrawalsContract(); + const { contractRpc, address, chainId } = useWithdrawalsContract(); // const { data: currentShareRate } = useLidoShareRate(); const swr = useLidoSWR( // TODO: use this fragment for expected eth calculation // currentShareRate - // ? ['swr:withdrawals-requests', account, chainId, currentShareRate] + // ? ['swr:withdrawals-requests', address, chainId, currentShareRate] // : false, - ['swr:withdrawals-requests', account, chainId], + ['swr:withdrawals-requests', address, chainId], async (...args: unknown[]) => { const account = args[1] as string; // const currentShareRate = args[3] as BigNumber; diff --git a/features/withdrawals/hooks/useNftDataByTxHash.ts b/features/withdrawals/hooks/useNftDataByTxHash.ts index 3f675ee89..7d2261063 100644 --- a/features/withdrawals/hooks/useNftDataByTxHash.ts +++ b/features/withdrawals/hooks/useNftDataByTxHash.ts @@ -14,15 +14,15 @@ type NFTApiData = { }; export const useNftDataByTxHash = (txHash: string | null) => { - const { contractRpc, account } = useWithdrawalsContract(); + const { contractRpc, address } = useWithdrawalsContract(); const { providerWeb3 } = useSDK(); const swrNftApiData = useLidoSWR( - account && txHash && providerWeb3 - ? ['swr:nft-data-by-tx-hash', txHash, account] + address && txHash && providerWeb3 + ? ['swr:nft-data-by-tx-hash', txHash, address] : null, async () => { - if (!txHash || !account || !providerWeb3) return null; + if (!txHash || !address || !providerWeb3) return null; const txReciept: TransactionReceipt = await providerWeb3.getTransactionReceipt(txHash); diff --git a/features/withdrawals/hooks/useWithdrawTxPrice.ts b/features/withdrawals/hooks/useWithdrawTxPrice.ts index 14af8fd1e..be986487e 100644 --- a/features/withdrawals/hooks/useWithdrawTxPrice.ts +++ b/features/withdrawals/hooks/useWithdrawTxPrice.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useWeb3 } from 'reef-knot/web3-react'; +import { useAccount } from 'wagmi'; import { TOKENS } from '@lido-sdk/constants'; import { useLidoSWR, useSDK } from '@lido-sdk/react'; @@ -109,7 +109,7 @@ export const useRequestTxPrice = ({ export const useClaimTxPrice = (requests: RequestStatusClaimable[]) => { const { contractRpc } = useWithdrawalsContract(); - const { account, chainId } = useWeb3(); + const { address, chainId } = useAccount(); const requestCount = requests.length || 1; const debouncedSortedSelectedRequests = useDebouncedValue(requests, 2000); @@ -118,13 +118,13 @@ export const useClaimTxPrice = (requests: RequestStatusClaimable[]) => { [ 'swr:claim-request-gas-limit', debouncedSortedSelectedRequests, - account, + address, chainId, ], async () => { if ( !chainId || - !account || + !address || !contractRpc || debouncedSortedSelectedRequests.length === 0 ) @@ -135,12 +135,12 @@ export const useClaimTxPrice = (requests: RequestStatusClaimable[]) => { .claimWithdrawals( sortedRequests.map((r) => r.id), sortedRequests.map((r) => r.hint), - { from: account }, + { from: address }, ) .catch((error) => { console.warn('Could not estimate gas for claim', { ids: sortedRequests.map((r) => r.id), - account, + address, error, }); return undefined; diff --git a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts index 9b50127ae..83500152b 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts @@ -1,19 +1,20 @@ +import { useCallback } from 'react'; import invariant from 'tiny-invariant'; +import { useAccount } from 'wagmi'; -import { useCallback } from 'react'; import { useSDK, useSTETHContractRPC, useWSTETHContractRPC, } from '@lido-sdk/react'; -import { useWeb3 } from 'reef-knot/web3-react'; -import { useUnwrapTxProcessing } from './use-unwrap-tx-processing'; -import { useTxModalStagesUnwrap } from './use-tx-modal-stages-unwrap'; import { isContract } from 'utils/isContract'; import { runWithTransactionLogger } from 'utils'; import type { UnwrapFormInputType } from '../unwrap-form-context'; +import { useUnwrapTxProcessing } from './use-unwrap-tx-processing'; +import { useTxModalStagesUnwrap } from './use-tx-modal-stages-unwrap'; + type UseUnwrapFormProcessorArgs = { onConfirm?: () => Promise; onRetry?: () => void; @@ -23,7 +24,7 @@ export const useUnwrapFormProcessor = ({ onConfirm, onRetry, }: UseUnwrapFormProcessorArgs) => { - const { account } = useWeb3(); + const { address } = useAccount(); const { providerWeb3 } = useSDK(); const processWrapTx = useUnwrapTxProcessing(); const stETHContractRPC = useSTETHContractRPC(); @@ -34,9 +35,9 @@ export const useUnwrapFormProcessor = ({ async ({ amount }: UnwrapFormInputType) => { try { invariant(amount, 'amount should be presented'); - invariant(account, 'address should be presented'); + invariant(address, 'address should be presented'); invariant(providerWeb3, 'provider should be presented'); - const isMultisig = await isContract(account, providerWeb3); + const isMultisig = await isContract(address, providerWeb3); const willReceive = await wstETHContractRPC.getStETHByWstETH(amount); txModalStages.sign(amount, willReceive); @@ -61,7 +62,7 @@ export const useUnwrapFormProcessor = ({ ); } - const stethBalance = await stETHContractRPC.balanceOf(account); + const stethBalance = await stETHContractRPC.balanceOf(address); await onConfirm?.(); txModalStages.success(stethBalance, txHash); @@ -73,7 +74,7 @@ export const useUnwrapFormProcessor = ({ } }, [ - account, + address, onConfirm, onRetry, processWrapTx, diff --git a/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx b/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx index 3a5dbec3f..68f8cc380 100644 --- a/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx +++ b/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx @@ -1,5 +1,5 @@ import { BigNumber } from 'ethers'; -import { useWeb3 } from 'reef-knot/web3-react'; +import { useAccount } from 'wagmi'; import { useLidoSWR, useSTETHContractRPC, @@ -13,7 +13,7 @@ import { STRATEGY_IMMUTABLE } from 'consts/swr-strategies'; export const useApproveGasLimit = () => { const steth = useSTETHContractRPC(); const wsteth = useWSTETHContractRPC(); - const { chainId } = useWeb3(); + const { chainId } = useAccount(); const { data } = useLidoSWR( ['swr:approve-wrap-gas-limit', chainId], diff --git a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts index 77cde9c3b..758de281d 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts @@ -1,10 +1,8 @@ +import { useCallback } from 'react'; import invariant from 'tiny-invariant'; +import { useAccount } from 'wagmi'; -import { useCallback } from 'react'; import { useSDK } from '@lido-sdk/react'; -import { useWeb3 } from 'reef-knot/web3-react'; -import { useWrapTxProcessing } from './use-wrap-tx-processing'; -import { useTxModalWrap } from './use-tx-modal-stages-wrap'; import { useWSTETHContractRPC } from '@lido-sdk/react'; import { runWithTransactionLogger } from 'utils'; @@ -14,6 +12,9 @@ import type { WrapFormInputType, } from '../wrap-form-context'; +import { useWrapTxProcessing } from './use-wrap-tx-processing'; +import { useTxModalWrap } from './use-tx-modal-stages-wrap'; + type UseWrapFormProcessorArgs = { approvalData: WrapFormApprovalData; onConfirm?: () => Promise; @@ -25,7 +26,7 @@ export const useWrapFormProcessor = ({ onConfirm, onRetry, }: UseWrapFormProcessorArgs) => { - const { account } = useWeb3(); + const { address } = useAccount(); const { providerWeb3 } = useSDK(); const processWrapTx = useWrapTxProcessing(); const { isApprovalNeededBeforeWrap, processApproveTx } = approvalData; @@ -36,9 +37,9 @@ export const useWrapFormProcessor = ({ async ({ amount, token }: WrapFormInputType) => { try { invariant(amount, 'amount should be presented'); - invariant(account, 'address should be presented'); + invariant(address, 'address should be presented'); invariant(providerWeb3, 'provider should be presented'); - const isMultisig = await isContract(account, providerWeb3); + const isMultisig = await isContract(address, providerWeb3); const willReceive = await wstETHContractRPC.getWstETHByStETH(amount); if (isApprovalNeededBeforeWrap) { @@ -78,7 +79,7 @@ export const useWrapFormProcessor = ({ ); } - const wstethBalance = await wstETHContractRPC.balanceOf(account); + const wstethBalance = await wstETHContractRPC.balanceOf(address); await onConfirm?.(); txModalStages.success(wstethBalance, txHash); @@ -90,7 +91,7 @@ export const useWrapFormProcessor = ({ } }, [ - account, + address, providerWeb3, wstETHContractRPC, isApprovalNeededBeforeWrap, diff --git a/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts b/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts index 1a3573e68..da2d754f8 100644 --- a/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts +++ b/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts @@ -1,4 +1,4 @@ -import { useWeb3 } from 'reef-knot/web3-react'; +import { useAccount } from 'wagmi'; import { useLidoSWR, useWSTETHContractRPC } from '@lido-sdk/react'; import { CHAINS } from '@lido-sdk/constants'; @@ -13,7 +13,7 @@ import { applyGasLimitRatio } from 'features/stake/stake-form/utils'; export const useWrapGasLimit = () => { const wsteth = useWSTETHContractRPC(); - const { chainId } = useWeb3(); + const { chainId } = useAccount(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { data } = useLidoSWR( diff --git a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts index ada13cc5e..6a14a634b 100644 --- a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts +++ b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts @@ -1,11 +1,13 @@ import { useMemo } from 'react'; -import { useWeb3 } from 'reef-knot/web3-react'; -import { useSDK } from '@lido-sdk/react'; -import { useApprove } from 'shared/hooks/useApprove'; - +import { useAccount } from 'wagmi'; import type { BigNumber } from 'ethers'; + import { getTokenAddress, TOKENS } from '@lido-sdk/constants'; +import { useSDK } from '@lido-sdk/react'; + import { TokensWrappable, TOKENS_TO_WRAP } from 'features/wsteth/shared/types'; +import { useApprove } from 'shared/hooks/useApprove'; +import { useDappStatus } from 'shared/hooks/use-dapp-status'; type UseWrapTxApproveArgs = { amount: BigNumber; @@ -13,7 +15,8 @@ type UseWrapTxApproveArgs = { }; export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { - const { active, account } = useWeb3(); + const { isDappActive } = useDappStatus(); + const { address } = useAccount(); const { chainId } = useSDK(); const [stethTokenAddress, wstethTokenAddress] = useMemo( @@ -33,11 +36,11 @@ export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { amount, stethTokenAddress, wstethTokenAddress, - account ? account : undefined, + address ? address : undefined, ); const isApprovalNeededBeforeWrap = - active && needsApprove && token === TOKENS_TO_WRAP.STETH; + isDappActive && needsApprove && token === TOKENS_TO_WRAP.STETH; return useMemo( () => ({ diff --git a/shared/hooks/useERC20PermitSignature.ts b/shared/hooks/useERC20PermitSignature.ts index 4ed13bdfe..cd4825f61 100644 --- a/shared/hooks/useERC20PermitSignature.ts +++ b/shared/hooks/useERC20PermitSignature.ts @@ -1,14 +1,12 @@ import { useCallback } from 'react'; +import { BigNumber, TypedDataDomain } from 'ethers'; import invariant from 'tiny-invariant'; +import { useAccount } from 'wagmi'; import { hexValue, splitSignature } from '@ethersproject/bytes'; import { MaxUint256 } from '@ethersproject/constants'; -import { BigNumber, TypedDataDomain } from 'ethers'; - import { useSDK } from '@lido-sdk/react'; - import { Erc20Abi, StethAbi } from '@lido-sdk/contracts'; -import { useWeb3 } from 'reef-knot/web3-react'; export type GatherPermitSignatureResult = { v: number; @@ -57,13 +55,13 @@ export const useERC20PermitSignature = < tokenProvider, spender, }: UseERC20PermitSignatureProps): UseERC20PermitSignatureResult => { - const { chainId, account } = useWeb3(); + const { address, chainId } = useAccount(); const { providerWeb3 } = useSDK(); const gatherPermitSignature = useCallback( async (amount: BigNumber) => { invariant(chainId, 'chainId is needed'); - invariant(account, 'account is needed'); + invariant(address, 'account is needed'); invariant(providerWeb3, 'providerWeb3 is needed'); invariant(tokenProvider, 'tokenProvider is needed'); @@ -86,10 +84,10 @@ export const useERC20PermitSignature = < verifyingContract: tokenProvider.address, }; } - const nonce = await tokenProvider.nonces(account); + const nonce = await tokenProvider.nonces(address); const message = { - owner: account, + owner: address, spender, value: amount.toString(), nonce: hexValue(nonce), @@ -113,12 +111,12 @@ export const useERC20PermitSignature = < deadline, chainId: chainId, nonce: message.nonce, - owner: account, + owner: address, spender, }; }); }, - [chainId, account, providerWeb3, tokenProvider, spender], + [chainId, address, providerWeb3, tokenProvider, spender], ); return { gatherPermitSignature }; diff --git a/shared/hooks/useIsMultisig.ts b/shared/hooks/useIsMultisig.ts index 5a15936b6..d4cd13374 100644 --- a/shared/hooks/useIsMultisig.ts +++ b/shared/hooks/useIsMultisig.ts @@ -1,10 +1,10 @@ -import { useWeb3 } from 'reef-knot/web3-react'; +import { useAccount } from 'wagmi'; import { useIsContract } from 'shared/hooks'; export const useIsMultisig = () => { - const { account } = useWeb3(); + const { address } = useAccount(); const { isContract: isMultisig, isLoading } = useIsContract( - account ?? undefined, + address ?? undefined, ); return { isMultisig, isLoading }; }; diff --git a/shared/wallet/wallet-modal/wallet-modal.tsx b/shared/wallet/wallet-modal/wallet-modal.tsx index 33361423b..747e98927 100644 --- a/shared/wallet/wallet-modal/wallet-modal.tsx +++ b/shared/wallet/wallet-modal/wallet-modal.tsx @@ -1,4 +1,6 @@ import { useCallback } from 'react'; +import { useAccount } from 'wagmi'; + import { ButtonIcon, Modal, @@ -9,7 +11,6 @@ import { } from '@lidofinance/lido-ui'; import { useEtherscanOpen } from '@lido-sdk/react'; import { useConnectorInfo, useDisconnect } from 'reef-knot/core-react'; -import { useWeb3 } from 'reef-knot/web3-react'; import type { ModalComponentType } from 'providers/modal-provider'; import { useCopyToClipboard } from 'shared/hooks'; @@ -24,7 +25,7 @@ import { } from './styles'; export const WalletModal: ModalComponentType = ({ onClose, ...props }) => { - const { account } = useWeb3(); + const { address } = useAccount(); const { connectorName } = useConnectorInfo(); const { disconnect } = useDisconnect(); @@ -33,8 +34,8 @@ export const WalletModal: ModalComponentType = ({ onClose, ...props }) => { onClose?.(); }, [disconnect, onClose]); - const handleCopy = useCopyToClipboard(account ?? ''); - const handleEtherscan = useEtherscanOpen(account ?? '', 'address'); + const handleCopy = useCopyToClipboard(address ?? ''); + const handleEtherscan = useEtherscanOpen(address ?? '', 'address'); return ( @@ -59,11 +60,11 @@ export const WalletModal: ModalComponentType = ({ onClose, ...props }) => { - +
From 013eb0d1d066a9d2c04c78c9796c696c8c707081 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Sun, 14 Jul 2024 12:35:14 +0300 Subject: [PATCH 03/40] refactor: remove a useWeb3Key --- features/stake/stake.tsx | 4 ++-- pages/withdrawals/[mode].tsx | 4 ++-- pages/wrap/[[...mode]].tsx | 7 ++++--- shared/hooks/use-wagmi-key.ts | 9 +++++++++ shared/hooks/useWeb3Key.ts | 10 ---------- 5 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 shared/hooks/use-wagmi-key.ts delete mode 100644 shared/hooks/useWeb3Key.ts diff --git a/features/stake/stake.tsx b/features/stake/stake.tsx index bdf554b15..64a1c0791 100644 --- a/features/stake/stake.tsx +++ b/features/stake/stake.tsx @@ -1,5 +1,5 @@ import { FaqPlaceholder } from 'features/ipfs'; -import { useWeb3Key } from 'shared/hooks/useWeb3Key'; +import { useWagmiKey } from 'shared/hooks/use-wagmi-key'; import NoSSRWrapper from 'shared/components/no-ssr-wrapper'; import { GoerliSunsetBanner } from 'shared/banners/goerli-sunset'; import { OnlyInfraRender } from 'shared/components/only-infra-render'; @@ -9,7 +9,7 @@ import { LidoStats } from './lido-stats/lido-stats'; import { StakeForm } from './stake-form'; export const Stake = () => { - const key = useWeb3Key(); + const key = useWagmiKey(); return ( <> diff --git a/pages/withdrawals/[mode].tsx b/pages/withdrawals/[mode].tsx index 54a5af0b1..a5447ddde 100644 --- a/pages/withdrawals/[mode].tsx +++ b/pages/withdrawals/[mode].tsx @@ -6,10 +6,10 @@ import { Layout } from 'shared/components'; import { WithdrawalsTabs } from 'features/withdrawals'; import { WithdrawalsProvider } from 'features/withdrawals/contexts/withdrawals-context'; -import { useWeb3Key } from 'shared/hooks/useWeb3Key'; +import { useWagmiKey } from 'shared/hooks/use-wagmi-key'; const Withdrawals: FC = ({ mode }) => { - const key = useWeb3Key(); + const key = useWagmiKey(); return ( = ({ mode }) => { - const key = useWeb3Key(); + const key = useWagmiKey(); return ( { + const { address, chainId } = useAccount(); + return `${address ?? 'NO_ADDRESS'}_${chainId ?? config.defaultChain}`; +}; diff --git a/shared/hooks/useWeb3Key.ts b/shared/hooks/useWeb3Key.ts deleted file mode 100644 index 074a47f67..000000000 --- a/shared/hooks/useWeb3Key.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useWeb3 } from 'reef-knot/web3-react'; - -import { config } from 'config'; - -// In order to simplify side effects of switching wallets/chains -// we can remount by this key, resetting all internal states -export const useWeb3Key = () => { - const { account, chainId } = useWeb3(); - return `${account ?? 'NO_ACCOUNT'}_${chainId ?? config.defaultChain}`; -}; From ab51d32bd6f9ac963208591190e1ea4c57621c46 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Sun, 14 Jul 2024 12:35:45 +0300 Subject: [PATCH 04/40] refactor(sdk-legacy provider): remove a useWeb3 --- providers/sdk-legacy.tsx | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/providers/sdk-legacy.tsx b/providers/sdk-legacy.tsx index 9c6203118..6cf3a9cfd 100644 --- a/providers/sdk-legacy.tsx +++ b/providers/sdk-legacy.tsx @@ -1,13 +1,14 @@ import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; -import { useSupportedChains, useWeb3 } from 'reef-knot/web3-react'; -import { useClient, useConfig } from 'wagmi'; +import { useReefKnotContext } from 'reef-knot/core-react'; +import { useSupportedChains } from 'reef-knot/web3-react'; +import { useAccount, useClient, useConfig } from 'wagmi'; +import { mainnet } from 'wagmi/chains'; import { Web3Provider } from '@ethersproject/providers'; import { ProviderSDK } from '@lido-sdk/react'; - -import { mainnet } from 'wagmi/chains'; import { getStaticRpcBatchProvider } from '@lido-sdk/providers'; -import { useReefKnotContext } from 'reef-knot/core-react'; + +import { useDappStatus } from 'shared/hooks/use-dapp-status'; const POLLING_INTERVAL = 12_000; @@ -21,8 +22,9 @@ export const SDKLegacyProvider = ({ defaultChainId, pollingInterval = POLLING_INTERVAL, }: SDKLegacyProviderProps) => { - const { chainId: web3ChainId = defaultChainId, account, active } = useWeb3(); + const { chainId: wagmiChainId = defaultChainId, address } = useAccount(); const { supportedChains } = useSupportedChains(); + const { isDappActive } = useDappStatus(); const config = useConfig(); const client = useClient(); const { rpc } = useReefKnotContext(); @@ -42,7 +44,7 @@ export const SDKLegacyProvider = ({ }; const getProviderValue = async () => { - if (!client || !account || !active) return undefined; + if (!client || !address || !isDappActive) return undefined; const { chain } = client; const providerTransport = await getProviderTransport(); @@ -67,7 +69,7 @@ export const SDKLegacyProvider = ({ return () => { isHookMounted = false; }; - }, [config, config.state, client, account, active, pollingInterval]); + }, [config, config.state, client, address, isDappActive, pollingInterval]); const supportedChainIds = useMemo( () => supportedChains.map((chain) => chain.chainId), @@ -75,10 +77,10 @@ export const SDKLegacyProvider = ({ ); const chainId = useMemo(() => { - return supportedChainIds.indexOf(web3ChainId) > -1 - ? web3ChainId + return supportedChainIds.indexOf(wagmiChainId) > -1 + ? wagmiChainId : defaultChainId; - }, [defaultChainId, supportedChainIds, web3ChainId]); + }, [defaultChainId, supportedChainIds, wagmiChainId]); const providerRpc = useMemo( () => getStaticRpcBatchProvider(chainId, rpc[chainId], 0, POLLING_INTERVAL), @@ -104,7 +106,7 @@ export const SDKLegacyProvider = ({ providerWeb3={providerWeb3} providerRpc={providerRpc} providerMainnetRpc={providerMainnetRpc} - account={account ?? undefined} + account={address ?? undefined} > {children} From 789ea0d31f5adc78f57d9bbd39d1f37e05e17a31 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Mon, 15 Jul 2024 09:56:18 +0300 Subject: [PATCH 05/40] refactor: get error connect from wagmi --- shared/wallet/fallback/useErrorMessage.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shared/wallet/fallback/useErrorMessage.ts b/shared/wallet/fallback/useErrorMessage.ts index 887bf0865..62d17ccd8 100644 --- a/shared/wallet/fallback/useErrorMessage.ts +++ b/shared/wallet/fallback/useErrorMessage.ts @@ -2,13 +2,14 @@ import { useConnectorInfo, getUnsupportedChainError, } from 'reef-knot/core-react'; -import { helpers, useSupportedChains, useWeb3 } from 'reef-knot/web3-react'; -import { useAccount, useConfig } from 'wagmi'; +import { helpers, useSupportedChains } from 'reef-knot/web3-react'; +import { useAccount, useConnect, useConfig } from 'wagmi'; export const useErrorMessage = (): string | undefined => { - const { error } = useWeb3(); const { chains } = useConfig(); const { isConnected } = useAccount(); + const { error } = useConnect(); + const { isUnsupported } = useSupportedChains(); const { isLedger } = useConnectorInfo(); From c9064bc3d69483d490c424ecb8cbf8e8a354bbe5 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Tue, 16 Jul 2024 14:42:34 +0300 Subject: [PATCH 06/40] docs: add todo --- providers/sdk-legacy.tsx | 1 + shared/wallet/fallback/useErrorMessage.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/providers/sdk-legacy.tsx b/providers/sdk-legacy.tsx index 6cf3a9cfd..b25bb4453 100644 --- a/providers/sdk-legacy.tsx +++ b/providers/sdk-legacy.tsx @@ -1,5 +1,6 @@ import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; import { useReefKnotContext } from 'reef-knot/core-react'; +// TODO: to remove the 'reef-knot/web3-react' after it will be deprecated import { useSupportedChains } from 'reef-knot/web3-react'; import { useAccount, useClient, useConfig } from 'wagmi'; import { mainnet } from 'wagmi/chains'; diff --git a/shared/wallet/fallback/useErrorMessage.ts b/shared/wallet/fallback/useErrorMessage.ts index 62d17ccd8..9c37a45fd 100644 --- a/shared/wallet/fallback/useErrorMessage.ts +++ b/shared/wallet/fallback/useErrorMessage.ts @@ -2,6 +2,7 @@ import { useConnectorInfo, getUnsupportedChainError, } from 'reef-knot/core-react'; +// TODO: to remove the 'reef-knot/web3-react' after it will be deprecated import { helpers, useSupportedChains } from 'reef-knot/web3-react'; import { useAccount, useConnect, useConfig } from 'wagmi'; From f6757b6117d4dd628fae0803f0931743053d8187 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Tue, 16 Jul 2024 15:02:36 +0300 Subject: [PATCH 07/40] fix: use address from wagmi --- shared/wallet/wallet-modal/wallet-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/wallet/wallet-modal/wallet-modal.tsx b/shared/wallet/wallet-modal/wallet-modal.tsx index 64bebaa24..bdb8a4bc9 100644 --- a/shared/wallet/wallet-modal/wallet-modal.tsx +++ b/shared/wallet/wallet-modal/wallet-modal.tsx @@ -39,10 +39,10 @@ export const WalletModal: ModalComponentType = ({ onClose, ...props }) => { useEffect(() => { // Close the modal if a wallet was somehow disconnected while the modal was open - if (account == null || account.length === 0) { + if (address == null || address.length === 0) { onClose?.(); } - }, [account, onClose]); + }, [address, onClose]); return ( From 357872b7e1a508bcdcdb65ff3e364be80cf53298 Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Fri, 19 Jul 2024 14:45:06 +0300 Subject: [PATCH 08/40] fix: after resolve conflicts --- shared/wallet/wallet-modal/wallet-modal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/wallet/wallet-modal/wallet-modal.tsx b/shared/wallet/wallet-modal/wallet-modal.tsx index a11fe0d2e..4a10a9594 100644 --- a/shared/wallet/wallet-modal/wallet-modal.tsx +++ b/shared/wallet/wallet-modal/wallet-modal.tsx @@ -45,10 +45,10 @@ export const WalletModal: ModalComponentType = ({ onClose, ...props }) => { useEffect(() => { // Close the modal if a wallet was somehow disconnected while the modal was open - if (account == null || account.length === 0) { + if (address == null || address.length === 0) { onClose?.(); } - }, [account, onClose]); + }, [address, onClose]); return ( From 7e864e0381820dd3b6eb3f2be19db8f6ec3ff29f Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 8 Aug 2024 12:37:47 +0700 Subject: [PATCH 09/40] fix: use ether balance dedupe --- .../stake-form-context/stake-form-context.tsx | 10 +-- features/wsteth/shared/wallet/wallet.tsx | 6 +- .../wrap/hooks/use-wrap-form-network-data.ts | 12 +--- package.json | 4 +- shared/hooks/use-balance.ts | 29 ++++++++ shared/wallet/button/button.tsx | 12 ++-- yarn.lock | 68 ++++++++++++------- 7 files changed, 90 insertions(+), 51 deletions(-) create mode 100644 shared/hooks/use-balance.ts diff --git a/features/stake/stake-form/stake-form-context/stake-form-context.tsx b/features/stake/stake-form/stake-form-context/stake-form-context.tsx index 0a0d6dd1c..f101ed140 100644 --- a/features/stake/stake-form/stake-form-context/stake-form-context.tsx +++ b/features/stake/stake-form/stake-form-context/stake-form-context.tsx @@ -11,7 +11,7 @@ import { import { useForm, FormProvider } from 'react-hook-form'; import { useRouter } from 'next/router'; -import { useEthereumBalance, useSTETHBalance } from '@lido-sdk/react'; +import { useSTETHBalance } from '@lido-sdk/react'; import { parseEther } from '@ethersproject/units'; import { @@ -39,6 +39,7 @@ import { import { useStake } from '../use-stake'; import { useStethSubmitGasLimit } from '../hooks'; +import { useEthereumBalance } from 'shared/hooks/use-balance'; // // Data context @@ -75,9 +76,10 @@ const useStakeFormNetworkData = (): StakeFormNetworkData => { const { data: etherBalance, - update: updateEtherBalance, - initialLoading: isEtherBalanceLoading, - } = useEthereumBalance(undefined, STRATEGY_LAZY); + refetch: updateEtherBalance, + isLoading: isEtherBalanceLoading, + } = useEthereumBalance(); + const { data: stakingLimitInfo, mutate: mutateStakeLimit, diff --git a/features/wsteth/shared/wallet/wallet.tsx b/features/wsteth/shared/wallet/wallet.tsx index 9f4b133f0..026084e46 100644 --- a/features/wsteth/shared/wallet/wallet.tsx +++ b/features/wsteth/shared/wallet/wallet.tsx @@ -4,7 +4,6 @@ import { Divider, Text } from '@lidofinance/lido-ui'; import { TOKENS } from '@lido-sdk/constants'; import { useSDK, - useEthereumBalance, useSTETHBalance, useWSTETHBalance, useTokenAddress, @@ -28,10 +27,11 @@ import { import { overrideWithQAMockBoolean } from 'utils/qa'; import { StyledCard } from './styles'; +import { useEthereumBalance } from 'shared/hooks/use-balance'; const WalletComponent: WalletComponentType = (props) => { const { account } = useSDK(); - const ethBalance = useEthereumBalance(undefined, STRATEGY_LAZY); + const ethBalance = useEthereumBalance(); const stethBalance = useSTETHBalance(STRATEGY_LAZY); const wstethBalance = useWSTETHBalance(STRATEGY_LAZY); @@ -46,7 +46,7 @@ const WalletComponent: WalletComponentType = (props) => { { const { isMultisig, isLoading: isMultisigLoading } = useIsMultisig(); - const { data: ethBalance, update: ethBalanceUpdate } = useEthereumBalance( - undefined, - STRATEGY_LAZY, - ); + const { data: ethBalance, refetch: ethBalanceUpdate } = useEthereumBalance(); const { data: stethBalance, update: stethBalanceUpdate } = useSTETHBalance(STRATEGY_LAZY); const { data: wstethBalance, update: wstethBalanceUpdate } = diff --git a/package.json b/package.json index 52a8523f8..2952a1dc3 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@lidofinance/next-pages": "^0.41.0", "@lidofinance/rpc": "^0.41.0", "@lidofinance/satanizer": "^0.41.0", - "@tanstack/react-query": "^5.48.0", + "@tanstack/react-query": "^5.51.21", "@wagmi/core": "^2.11.6", "bignumber.js": "9.1.0", "copy-to-clipboard": "^3.3.1", @@ -79,7 +79,7 @@ "tiny-async-pool": "^1.2.0", "tiny-invariant": "^1.1.0", "uuid": "^8.3.2", - "viem": "2.13.3", + "viem": "2.18.8", "wagmi": "2.12.2" }, "devDependencies": { diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts new file mode 100644 index 000000000..8b0dd8ae2 --- /dev/null +++ b/shared/hooks/use-balance.ts @@ -0,0 +1,29 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { BigNumber } from 'ethers'; +import { useEffect } from 'react'; +import { useBlockNumber, useBalance, useAccount } from 'wagmi'; +import type { GetBalanceData } from 'wagmi/query'; + +const dataToBN = (data: GetBalanceData) => + BigNumber.from(data.value.toString()); + +export const useEthereumBalance = () => { + const queryClient = useQueryClient(); + const { address } = useAccount(); + const { data: blockNumber } = useBlockNumber({ watch: true }); + const queryData = useBalance({ + address, + query: { select: dataToBN, staleTime: 7000, enabled: !!address }, + }); + + useEffect(() => { + void queryClient.invalidateQueries( + { queryKey: queryData.queryKey }, + // this tells RQ to not force another refetch if this query is already revalidating + // dedups rpc requests + { cancelRefetch: false }, + ); + }, [blockNumber, queryClient, queryData.queryKey]); + + return queryData; +}; diff --git a/shared/wallet/button/button.tsx b/shared/wallet/button/button.tsx index 17f80c380..7d6e9a451 100644 --- a/shared/wallet/button/button.tsx +++ b/shared/wallet/button/button.tsx @@ -1,9 +1,7 @@ import { FC } from 'react'; import { useAccount } from 'wagmi'; import { ButtonProps, useBreakpoint } from '@lidofinance/lido-ui'; -import { useEthereumBalance } from '@lido-sdk/react'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { FormatToken } from 'shared/formatters'; import { useDappStatus } from 'shared/hooks/use-dapp-status'; @@ -16,6 +14,7 @@ import { WalledButtonBalanceStyle, WalledButtonLoaderStyle, } from './styles'; +import { useEthereumBalance } from 'shared/hooks/use-balance'; export const Button: FC = (props) => { const { onClick, ...rest } = props; @@ -25,10 +24,7 @@ export const Button: FC = (props) => { const { isDappActive } = useDappStatus(); const { openModal } = useWalletModal(); - const { data: balance, initialLoading } = useEthereumBalance( - undefined, - STRATEGY_LAZY, - ); + const { data: balance, isLoading } = useEthereumBalance(); return ( = (props) => { variant="text" color="secondary" onClick={() => openModal({})} - $isAddPaddingLeft={!initialLoading && !isDappActive && !isMobile} + $isAddPaddingLeft={!isLoading && !isDappActive && !isMobile} {...rest} > - {initialLoading ? ( + {isLoading ? ( ) : ( isDappActive && ( diff --git a/yarn.lock b/yarn.lock index 9f1bb5b2a..ea9ccd246 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1005,6 +1005,13 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== +"@babel/runtime@^7.12.5": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb" + integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" @@ -2594,12 +2601,19 @@ dependencies: "@noble/hashes" "1.4.0" +"@noble/curves@^1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + "@noble/hashes@1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@1.4.0", "@noble/hashes@^1.3.1", "@noble/hashes@~1.4.0": +"@noble/hashes@1.4.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.4.0", "@noble/hashes@~1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== @@ -3342,17 +3356,17 @@ dependencies: tslib "^2.4.0" -"@tanstack/query-core@5.48.0": - version "5.48.0" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.48.0.tgz#a3308ec925d8c16d64c789899d6c084c2fe30cbc" - integrity sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q== +"@tanstack/query-core@5.51.21": + version "5.51.21" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.51.21.tgz#a510469c6c30d3de2a8b8798e340169a4b0fd08f" + integrity sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw== -"@tanstack/react-query@^5.48.0": - version "5.48.0" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.48.0.tgz#7890620272b48aeb278498dfe082f27518f3ac6d" - integrity sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw== +"@tanstack/react-query@^5.51.21": + version "5.51.21" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.51.21.tgz#cdd14677bcc809a83e01b6c38842c841ce7420af" + integrity sha512-Q/V81x3sAYgCsxjwOkfLXfrmoG+FmDhLeHH5okC/Bp8Aaw2c33lbEo/mMcMnkxUPVtB2FLpzHT0tq3c+OlZEbw== dependencies: - "@tanstack/query-core" "5.48.0" + "@tanstack/query-core" "5.51.21" "@trysound/sax@0.2.0": version "0.2.0" @@ -4128,11 +4142,6 @@ abitype@0.9.8: resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" integrity sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ== -abitype@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" - integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== - abitype@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.5.tgz#29d0daa3eea867ca90f7e4123144c1d1270774b6" @@ -10948,19 +10957,20 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -viem@2.13.3: - version "2.13.3" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.13.3.tgz#950426e4cacf5e12fab2c202a339371901712481" - integrity sha512-3tlwDRKHSelupFjbFMdUxF41f79ktyH2F9PAQ9Dltbs1DpdDlR1x+Ksa0th6qkyjjAbpDZP3F5nMTJv/1GVPdQ== +viem@2.18.8: + version "2.18.8" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.18.8.tgz#5e65050ddc3bdc0f928c0ca22b33bc720bf36639" + integrity sha512-Fi5d9fd/LBiVtJ5eV2c99yrdt4dJH5Vbkf2JajwCqHYuV4ErSk/sm+L6Ru3rzT67rfRHSOQibTZxByEBua/WLw== dependencies: "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@scure/bip32" "1.3.2" - "@scure/bip39" "1.2.1" - abitype "1.0.0" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + abitype "1.0.5" isows "1.0.4" - ws "8.13.0" + webauthn-p256 "0.0.5" + ws "8.17.1" viem@^1.1.4: version "1.21.4" @@ -11006,6 +11016,14 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +webauthn-p256@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/webauthn-p256/-/webauthn-p256-0.0.5.tgz#0baebd2ba8a414b21cc09c0d40f9dd0be96a06bd" + integrity sha512-drMGNWKdaixZNobeORVIqq7k5DsRC9FnG201K2QjeOoQLmtSDaSsVZdkg6n5jUALJKcAG++zBPJXmv6hy0nWFg== + dependencies: + "@noble/curves" "^1.4.0" + "@noble/hashes" "^1.4.0" + "webextension-polyfill@>=0.10.0 <1.0": version "0.12.0" resolved "https://registry.yarnpkg.com/webextension-polyfill/-/webextension-polyfill-0.12.0.tgz#f62c57d2cd42524e9fbdcee494c034cae34a3d69" From 7af2cbd3a36580a516a87a953e11e2a8a8aba1dc Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Thu, 8 Aug 2024 10:51:18 +0300 Subject: [PATCH 10/40] fix: tests (temply) --- test/smoke.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke.spec.ts b/test/smoke.spec.ts index 3f2d2a83c..9a65dff84 100644 --- a/test/smoke.spec.ts +++ b/test/smoke.spec.ts @@ -19,7 +19,7 @@ test.describe('Smoke GET', () => { const resp = await request.get(element.uri); if (element.isDeprecated) { - expect([299, 410]).toContain(resp.status()); + expect([200, 299, 410]).toContain(resp.status()); if (resp.status() === 299) { expect(resp.headers()).toHaveProperty('warning'); expect(resp.headers()).toHaveProperty('deprecation'); From 448ed06eead34ae66ca955df1b275f927bad5a5c Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 8 Aug 2024 15:16:34 +0700 Subject: [PATCH 11/40] feat: custom viem web3 transport --- providers/web3.tsx | 31 ++++++---- shared/hooks/use-balance.ts | 2 +- utils/use-web3-transport.ts | 109 ++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 utils/use-web3-transport.ts diff --git a/providers/web3.tsx b/providers/web3.tsx index c13000bae..92b3f6b48 100644 --- a/providers/web3.tsx +++ b/providers/web3.tsx @@ -1,6 +1,6 @@ -import { FC, PropsWithChildren, useMemo } from 'react'; +import { FC, PropsWithChildren, useEffect, useMemo } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { http, WagmiProvider, createConfig } from 'wagmi'; +import { WagmiProvider, createConfig, useConnections } from 'wagmi'; import * as wagmiChains from 'wagmi/chains'; import { AutoConnect, @@ -16,6 +16,7 @@ import { CHAINS } from 'consts/chains'; import { ConnectWalletModal } from 'shared/wallet/connect-wallet-modal'; import { SDKLegacyProvider } from './sdk-legacy'; +import { useWeb3Transport } from 'utils/use-web3-transport'; type ChainsList = [wagmiChains.Chain, ...wagmiChains.Chain[]]; @@ -63,15 +64,21 @@ const Web3Provider: FC = ({ children }) => { return getWalletsDataList({ walletsList: WalletsListEthereum, rpc: backendRPC, - walletconnectProjectId: walletconnectProjectId, - defaultChain: defaultChain, + walletconnectProjectId, + defaultChain, }); }, [backendRPC, defaultChain, walletconnectProjectId]); + const { transportMap, onActiveConnection } = useWeb3Transport( + supportedChains, + backendRPC, + ); + const wagmiConfig = useMemo(() => { return createConfig({ chains: supportedChains, ssr: true, + connectors: [], batch: { // eth_call's will be batched via multicall contract every 100ms multicall: { @@ -80,15 +87,15 @@ const Web3Provider: FC = ({ children }) => { }, multiInjectedProviderDiscovery: false, pollingInterval: config.PROVIDER_POLLING_INTERVAL, - transports: supportedChains.reduce( - (res, curr) => ({ - ...res, - [curr.id]: http(backendRPC[curr.id], { batch: true }), - }), - {}, - ), + transports: transportMap, }); - }, [supportedChains, backendRPC]); + }, [supportedChains, transportMap]); + + const [activeConnection] = useConnections({ config: wagmiConfig }); + + useEffect(() => { + void onActiveConnection(activeConnection ?? null); + }, [activeConnection, onActiveConnection]); return ( // default wagmi autoConnect, MUST be false in our case, because we use custom autoConnect from Reef Knot diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 8b0dd8ae2..2b53e84d1 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -10,7 +10,7 @@ const dataToBN = (data: GetBalanceData) => export const useEthereumBalance = () => { const queryClient = useQueryClient(); const { address } = useAccount(); - const { data: blockNumber } = useBlockNumber({ watch: true }); + const { data: blockNumber } = useBlockNumber({ watch: !!address }); const queryData = useBalance({ address, query: { select: dataToBN, staleTime: 7000, enabled: !!address }, diff --git a/utils/use-web3-transport.ts b/utils/use-web3-transport.ts new file mode 100644 index 000000000..2cd7761a9 --- /dev/null +++ b/utils/use-web3-transport.ts @@ -0,0 +1,109 @@ +// TODO: move this to dedicated web3 configuration module + +import { useMemo, useCallback } from 'react'; +import { + Transport, + fallback, + createTransport, + http, + EIP1193Provider, + custom, + Chain, +} from 'viem'; +import { Connection } from 'wagmi'; + +// Viem transport wrapper that allows runtime changes via setter +const runtimeMutableTransport = ( + mainTransports: Transport[], +): [Transport, (t: Transport | null) => void] => { + let withInjectedTransport: Transport | null = null; + return [ + (params) => { + const defaultTransport = fallback(mainTransports)(params); + + return createTransport( + { + key: 'RuntimeMutableTransport', + name: 'RuntimeMutableTransport', + //@ts-expect-error invalid typings + async request(requestParams, options) { + const transport = withInjectedTransport + ? withInjectedTransport(params) + : defaultTransport; + return transport.request(requestParams, options); + }, + type: 'fallback', + }, + { + transports: defaultTransport.value?.transports, + }, + ); + }, + (injectedTransport: Transport | null) => { + if (injectedTransport) { + withInjectedTransport = fallback([ + injectedTransport, + ...mainTransports, + ]); + } else { + withInjectedTransport = null; + } + }, + ]; +}; + +// returns Viem transport map that uses browser wallet RPC provider when avaliable fallbacked by our RPC +export const useWeb3Transport = ( + supportedChains: Chain[], + backendRpcMap: Record, +) => { + const { transportMap, setTransportMap } = useMemo(() => { + return supportedChains.reduce( + ({ transportMap, setTransportMap }, chain) => { + const [transport, setTransport] = runtimeMutableTransport([ + http(backendRpcMap[chain.id], { + batch: true, + name: backendRpcMap[chain.id], + }), + http(), + ]); + return { + transportMap: { + ...transportMap, + [chain.id]: transport, + }, + setTransportMap: { + ...setTransportMap, + [chain.id]: setTransport, + }, + }; + }, + { + transportMap: {} as Record, + setTransportMap: {} as Record void>, + }, + ); + }, [supportedChains, backendRpcMap]); + + const onActiveConnection = useCallback( + async (activeConnection: Connection | null) => { + for (const chain of supportedChains) { + const setTransport = setTransportMap[chain.id]; + if ( + activeConnection && + chain.id === activeConnection.chainId && + activeConnection.connector.type === 'injected' + ) { + const provider = (await activeConnection.connector?.getProvider?.({ + chainId: chain.id, + })) as EIP1193Provider | undefined; + + setTransport(provider ? custom(provider) : null); + } else setTransport(null); + } + }, + [setTransportMap, supportedChains], + ); + + return { transportMap, onActiveConnection }; +}; From b4610f135805a83018854706da39dd01ba91aee3 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 8 Aug 2024 17:39:56 +0700 Subject: [PATCH 12/40] feat: use token balance from sdk --- .../rewardsListContent/RewardsListContent.tsx | 7 +- .../stake-form-context/stake-form-context.tsx | 10 +- .../use-request-form-data-context-value.ts | 20 ++-- features/wsteth/shared/wallet/wallet.tsx | 22 ++-- .../hooks/use-unwrap-form-network-data.ts | 10 +- .../wrap/hooks/use-wrap-form-network-data.ts | 15 +-- package.json | 1 + providers/index.tsx | 13 ++- providers/lido-sdk.tsx | 45 ++++++++ shared/hooks/use-balance.ts | 103 +++++++++++++++++- yarn.lock | 45 +++++++- 11 files changed, 236 insertions(+), 55 deletions(-) create mode 100644 providers/lido-sdk.tsx diff --git a/features/rewards/components/rewardsListContent/RewardsListContent.tsx b/features/rewards/components/rewardsListContent/RewardsListContent.tsx index ff5d9de6e..1bce218c7 100644 --- a/features/rewards/components/rewardsListContent/RewardsListContent.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListContent.tsx @@ -1,13 +1,12 @@ import { FC } from 'react'; import { Loader, Divider } from '@lidofinance/lido-ui'; -import { useSTETHBalance } from '@lido-sdk/react'; import { Zero } from '@ethersproject/constants'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { useRewardsHistory } from 'features/rewards/hooks'; import { ErrorBlockNoSteth } from 'features/rewards/components/errorBlocks/ErrorBlockNoSteth'; import { RewardsTable } from 'features/rewards/components/rewardsTable'; import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useStethBalance } from 'shared/hooks/use-balance'; import { RewardsListsEmpty } from './RewardsListsEmpty'; import { RewardsListErrorMessage } from './RewardsListErrorMessage'; @@ -28,8 +27,8 @@ export const RewardsListContent: FC = () => { setPage, isLagging, } = useRewardsHistory(); - const { data: stethBalance, initialLoading: isStethBalanceLoading } = - useSTETHBalance(STRATEGY_LAZY); + const { data: stethBalance, isLoading: isStethBalanceLoading } = + useStethBalance(); const hasSteth = stethBalance?.gt(Zero); if (!isDappActive || (!data && !initialLoading && !error)) diff --git a/features/stake/stake-form/stake-form-context/stake-form-context.tsx b/features/stake/stake-form/stake-form-context/stake-form-context.tsx index f101ed140..5db80bf76 100644 --- a/features/stake/stake-form/stake-form-context/stake-form-context.tsx +++ b/features/stake/stake-form/stake-form-context/stake-form-context.tsx @@ -11,7 +11,6 @@ import { import { useForm, FormProvider } from 'react-hook-form'; import { useRouter } from 'next/router'; -import { useSTETHBalance } from '@lido-sdk/react'; import { parseEther } from '@ethersproject/units'; import { @@ -24,7 +23,6 @@ import { useMaxGasPrice } from 'shared/hooks'; import { useIsMultisig } from 'shared/hooks/useIsMultisig'; import { useFormControllerRetry } from 'shared/hook-form/form-controller/use-form-controller-retry-delegate'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { config } from 'config'; import { @@ -39,7 +37,7 @@ import { import { useStake } from '../use-stake'; import { useStethSubmitGasLimit } from '../hooks'; -import { useEthereumBalance } from 'shared/hooks/use-balance'; +import { useEthereumBalance, useStethBalance } from 'shared/hooks/use-balance'; // // Data context @@ -61,9 +59,9 @@ export const useStakeFormData = () => { const useStakeFormNetworkData = (): StakeFormNetworkData => { const { data: stethBalance, - update: updateStethBalance, - initialLoading: isStethBalanceLoading, - } = useSTETHBalance(STRATEGY_LAZY); + refetch: updateStethBalance, + isLoading: isStethBalanceLoading, + } = useStethBalance(); const { isMultisig, isLoading: isMultisigLoading } = useIsMultisig(); const gasLimit = useStethSubmitGasLimit(); const { maxGasPrice, initialLoading: isMaxGasPriceLoading } = diff --git a/features/withdrawals/request/request-form-context/use-request-form-data-context-value.ts b/features/withdrawals/request/request-form-context/use-request-form-data-context-value.ts index 2d88b5683..86bddb7ac 100644 --- a/features/withdrawals/request/request-form-context/use-request-form-data-context-value.ts +++ b/features/withdrawals/request/request-form-context/use-request-form-data-context-value.ts @@ -1,14 +1,10 @@ -import { - useSTETHContractRPC, - useSTETHBalance, - useWSTETHBalance, - useContractSWR, -} from '@lido-sdk/react'; +import { useSTETHContractRPC, useContractSWR } from '@lido-sdk/react'; import { useClaimData } from 'features/withdrawals/contexts/claim-data-context'; import { useWithdrawals } from 'features/withdrawals/contexts/withdrawals-context'; import { useUnfinalizedStETH } from 'features/withdrawals/hooks'; import { useCallback, useMemo } from 'react'; import { useWstethBySteth } from 'shared/hooks'; +import { useStethBalance, useWstethBalance } from 'shared/hooks/use-balance'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; // Provides all data fetching for form to function @@ -28,14 +24,14 @@ export const useRequestFormDataContextValue = () => { } = useWithdrawals(); const { data: balanceSteth, - update: stethUpdate, - initialLoading: isStethBalanceLoading, - } = useSTETHBalance(STRATEGY_LAZY); + refetch: stethUpdate, + isLoading: isStethBalanceLoading, + } = useStethBalance(); const { data: balanceWSteth, - update: wstethUpdate, - initialLoading: isWstethBalanceLoading, - } = useWSTETHBalance(STRATEGY_LAZY); + refetch: wstethUpdate, + isLoading: isWstethBalanceLoading, + } = useWstethBalance(); const { data: unfinalizedStETH, update: unfinalizedStETHUpdate, diff --git a/features/wsteth/shared/wallet/wallet.tsx b/features/wsteth/shared/wallet/wallet.tsx index 026084e46..a5cea6a8e 100644 --- a/features/wsteth/shared/wallet/wallet.tsx +++ b/features/wsteth/shared/wallet/wallet.tsx @@ -2,16 +2,10 @@ import { memo } from 'react'; import { Divider, Text } from '@lidofinance/lido-ui'; import { TOKENS } from '@lido-sdk/constants'; -import { - useSDK, - useSTETHBalance, - useWSTETHBalance, - useTokenAddress, -} from '@lido-sdk/react'; +import { useSDK, useTokenAddress } from '@lido-sdk/react'; import { useConfig } from 'config'; import { CHAINS } from 'consts/chains'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { FormatToken } from 'shared/formatters'; import { TokenToWallet } from 'shared/components'; import { useWstethBySteth, useStethByWsteth } from 'shared/hooks'; @@ -27,13 +21,17 @@ import { import { overrideWithQAMockBoolean } from 'utils/qa'; import { StyledCard } from './styles'; -import { useEthereumBalance } from 'shared/hooks/use-balance'; +import { + useEthereumBalance, + useStethBalance, + useWstethBalance, +} from 'shared/hooks/use-balance'; const WalletComponent: WalletComponentType = (props) => { const { account } = useSDK(); const ethBalance = useEthereumBalance(); - const stethBalance = useSTETHBalance(STRATEGY_LAZY); - const wstethBalance = useWSTETHBalance(STRATEGY_LAZY); + const stethBalance = useStethBalance(); + const wstethBalance = useWstethBalance(); const stethAddress = useTokenAddress(TOKENS.STETH); const wstethAddress = useTokenAddress(TOKENS.WSTETH); @@ -62,7 +60,7 @@ const WalletComponent: WalletComponentType = (props) => { { { const { isMultisig } = useIsMultisig(); - const { data: stethBalance, update: stethBalanceUpdate } = - useSTETHBalance(STRATEGY_LAZY); - const { data: wstethBalance, update: wstethBalanceUpdate } = - useWSTETHBalance(STRATEGY_LAZY); + const { data: stethBalance, refetch: stethBalanceUpdate } = useStethBalance(); + const { data: wstethBalance, refetch: wstethBalanceUpdate } = + useWstethBalance(); const revalidateUnwrapFormData = useCallback(async () => { await Promise.allSettled([stethBalanceUpdate(), wstethBalanceUpdate()]); diff --git a/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts b/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts index 5ab0d38f0..b0da4caf7 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts @@ -1,23 +1,24 @@ import { useCallback, useMemo } from 'react'; -import { useWSTETHBalance, useSTETHBalance } from '@lido-sdk/react'; import { config } from 'config'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { useIsMultisig } from 'shared/hooks/useIsMultisig'; import { useTokenMaxAmount } from 'shared/hooks/use-token-max-amount'; import { useMaxGasPrice, useStakingLimitInfo } from 'shared/hooks'; import { useWrapGasLimit } from './use-wrap-gas-limit'; -import { useEthereumBalance } from 'shared/hooks/use-balance'; +import { + useEthereumBalance, + useStethBalance, + useWstethBalance, +} from 'shared/hooks/use-balance'; // Provides all data fetching for form to function export const useWrapFormNetworkData = () => { const { isMultisig, isLoading: isMultisigLoading } = useIsMultisig(); const { data: ethBalance, refetch: ethBalanceUpdate } = useEthereumBalance(); - const { data: stethBalance, update: stethBalanceUpdate } = - useSTETHBalance(STRATEGY_LAZY); - const { data: wstethBalance, update: wstethBalanceUpdate } = - useWSTETHBalance(STRATEGY_LAZY); + const { data: stethBalance, refetch: stethBalanceUpdate } = useStethBalance(); + const { data: wstethBalance, refetch: wstethBalanceUpdate } = + useWstethBalance(); const { data: stakeLimitInfo, mutate: stakeLimitInfoUpdate } = useStakingLimitInfo(); diff --git a/package.json b/package.json index 2952a1dc3..13e9ae7f1 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@lidofinance/api-rpc": "^0.41.0", "@lidofinance/eth-api-providers": "^0.41.0", "@lidofinance/eth-providers": "^0.41.0", + "@lidofinance/lido-ethereum-sdk": "^3.3.1", "@lidofinance/lido-ui": "^3.26.0", "@lidofinance/next-api-wrapper": "^0.41.0", "@lidofinance/next-ip-rate-limit": "^0.41.0", diff --git a/providers/index.tsx b/providers/index.tsx index 5da5a1bdc..5d6e2c72a 100644 --- a/providers/index.tsx +++ b/providers/index.tsx @@ -9,6 +9,7 @@ import { IPFSInfoBoxStatusesProvider } from './ipfs-info-box-statuses'; import { InpageNavigationProvider } from './inpage-navigation'; import { ModalProvider } from './modal-provider'; import Web3Provider from './web3'; +import { LidoSDKProvider } from './lido-sdk'; export const Providers: FC = ({ children }) => ( @@ -16,11 +17,13 @@ export const Providers: FC = ({ children }) => ( - - - {children} - - + + + + {children} + + + diff --git a/providers/lido-sdk.tsx b/providers/lido-sdk.tsx new file mode 100644 index 000000000..b0c50e82a --- /dev/null +++ b/providers/lido-sdk.tsx @@ -0,0 +1,45 @@ +import { createContext, useContext, useMemo } from 'react'; +import { LidoSDKCore } from '@lidofinance/lido-ethereum-sdk/core'; +import { + LidoSDKstETH, + LidoSDKwstETH, +} from '@lidofinance/lido-ethereum-sdk/erc20'; +import invariant from 'tiny-invariant'; +import { useChainId, useClient, useConnectorClient } from 'wagmi'; + +type LidoSDKContextValue = { + core: LidoSDKCore; + steth: LidoSDKstETH; + wsteth: LidoSDKwstETH; +}; + +const LidoSDKContext = createContext(null); +LidoSDKContext.displayName = 'LidoSDKContext'; + +export const useLidoSDK = () => { + const value = useContext(LidoSDKContext); + invariant(value, 'useLidoSDK was used outside of LidoSDKProvider'); + return value; +}; + +export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { + const publicClient = useClient(); + const chainId = useChainId(); + const { data: walletClient } = useConnectorClient(); + const sdk = useMemo(() => { + const core = new LidoSDKCore({ + chainId, + logMode: 'none', + rpcProvider: publicClient as any, + web3Provider: walletClient as any, + }); + + const steth = new LidoSDKstETH({ core }); + const wsteth = new LidoSDKwstETH({ core }); + + return { core, steth, wsteth }; + }, [chainId, publicClient, walletClient]); + return ( + {children} + ); +}; diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 2b53e84d1..6ff0a4665 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -1,12 +1,28 @@ -import { useQueryClient } from '@tanstack/react-query'; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { BigNumber } from 'ethers'; +import { useLidoSDK } from 'providers/lido-sdk'; import { useEffect } from 'react'; -import { useBlockNumber, useBalance, useAccount } from 'wagmi'; +import { + useBlockNumber, + useBalance, + useAccount, + useReadContract, + useWatchContractEvent, +} from 'wagmi'; + +import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; + +import {} from '@lidofinance/lido-ethereum-sdk/erc20'; + import type { GetBalanceData } from 'wagmi/query'; +import { Address } from 'viem'; const dataToBN = (data: GetBalanceData) => BigNumber.from(data.value.toString()); +const NativeToBN = (data: bigint) => BigNumber.from(data.toString()); + export const useEthereumBalance = () => { const queryClient = useQueryClient(); const { address } = useAccount(); @@ -27,3 +43,86 @@ export const useEthereumBalance = () => { return queryData; }; + +type TokenContract = Awaited< + ReturnType['getContract']> +>; + +const useTokenBalance = (contract: TokenContract, address?: Address) => { + const queryClient = useQueryClient(); + const balanceQuery = useReadContract({ + abi: contract?.abi, + address: contract?.address, + functionName: 'balanceOf', + args: address && [address], + query: { enabled: !!address, select: NativeToBN }, + }); + + useWatchContractEvent({ + abi: contract?.abi, + address: contract?.address, + eventName: 'Transfer', + enabled: !!(address && balanceQuery.data), + args: { from: address! }, + onLogs: () => { + void queryClient.invalidateQueries( + { queryKey: balanceQuery.queryKey }, + { cancelRefetch: false }, + ); + }, + }); + + useWatchContractEvent({ + abi: contract?.abi, + address: contract?.address, + eventName: 'Transfer', + enabled: !!(address && balanceQuery.data), + args: { to: address! }, + onLogs: () => { + void queryClient.invalidateQueries( + { queryKey: balanceQuery.queryKey }, + { cancelRefetch: false }, + ); + }, + }); + + return balanceQuery; +}; + +export const useStethBalance = () => { + const { address } = useAccount(); + + const { steth, core } = useLidoSDK(); + + const { data: contractData, isLoading } = useQuery({ + queryKey: ['steth-contract', core.chainId], + enabled: !!address, + staleTime: Infinity, + queryFn: async () => steth.getContract(), + }); + + const contract = contractData as NonNullable; + + const balanceData = useTokenBalance(contract, address); + + return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; +}; + +export const useWstethBalance = () => { + const { address } = useAccount(); + + const { wsteth, core } = useLidoSDK(); + + const { data: contractData, isLoading } = useQuery({ + queryKey: ['wsteth-contract', core.chainId], + enabled: !!address, + staleTime: Infinity, + queryFn: async () => wsteth.getContract(), + }); + + const contract = contractData as NonNullable; + + const balanceData = useTokenBalance(contract, address); + + return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; +}; diff --git a/yarn.lock b/yarn.lock index ea9ccd246..c5e13a8c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1715,6 +1715,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@graphql-typed-document-node/core@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + "@humanwhocodes/config-array@^0.11.13": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -2200,6 +2205,16 @@ resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.41.0.tgz#31e8c4abbc22375ea0b1eb54a34eee72f175e906" integrity sha512-qz/+C1J6soJjtrAOBIlfTdez60lK9lS8jqvdUSEGPHMr88Pg+6p77Zh5kzQynGXuwMeg7xKVqB5uMLFJPAOGGA== +"@lidofinance/lido-ethereum-sdk@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@lidofinance/lido-ethereum-sdk/-/lido-ethereum-sdk-3.3.1.tgz#5daabb594dd17e9e1af61ca0213410b7132232c1" + integrity sha512-lBnP5zHDUyOK0p9YS6MLp04oVgYo9TNd+mrvjJLFzrXl3UrrucRGkKr5Rq1qBEvT6D1AF6+8zv2BbCDdP2Eksw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + graphql "^16.8.1" + graphql-request "^6.1.0" + viem "^2.0.6" + "@lidofinance/lido-ui@^3.18.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lidofinance/lido-ui/-/lido-ui-3.21.0.tgz#da772e44ca7e96a062187858fe896ebc7e3d5cd1" @@ -5173,7 +5188,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@^3.1.4: +cross-fetch@^3.1.4, cross-fetch@^3.1.5: version "3.1.8" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== @@ -6576,6 +6591,19 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +graphql-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + +graphql@^16.8.1: + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + gray-matter@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" @@ -10986,6 +11014,21 @@ viem@^1.1.4: isows "1.0.3" ws "8.13.0" +viem@^2.0.6: + version "2.19.2" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.19.2.tgz#11f03621fd0d0d742f04e3da30fa49093a3cf612" + integrity sha512-BrR7fEEpuu9Om7obQGThb4BEu00PPHPKaUx+snB/F6yBZtr34FdXCPnphr+S73W2iIu/mt3yaRkfkLlD6a1R5g== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + abitype "1.0.5" + isows "1.0.4" + webauthn-p256 "0.0.5" + ws "8.17.1" + viem@^2.1.1: version "2.17.9" resolved "https://registry.yarnpkg.com/viem/-/viem-2.17.9.tgz#40ffd00a31621c8efdc4d49a58d5d30dc2d38d83" From 686897f64967e1659198dac52c4d5b7eb657e8f6 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 8 Aug 2024 17:50:50 +0700 Subject: [PATCH 13/40] fix: use polling --- shared/hooks/use-balance.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 6ff0a4665..c3817301d 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -13,8 +13,6 @@ import { import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; -import {} from '@lidofinance/lido-ethereum-sdk/erc20'; - import type { GetBalanceData } from 'wagmi/query'; import { Address } from 'viem'; @@ -62,6 +60,8 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { abi: contract?.abi, address: contract?.address, eventName: 'Transfer', + poll: true, + enabled: !!(address && balanceQuery.data), args: { from: address! }, onLogs: () => { @@ -76,6 +76,7 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { abi: contract?.abi, address: contract?.address, eventName: 'Transfer', + poll: true, enabled: !!(address && balanceQuery.data), args: { to: address! }, onLogs: () => { From 95ce4569ea4171549ebd0c89a9b677cb86b9d2c1 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 9 Aug 2024 16:46:00 +0700 Subject: [PATCH 14/40] feat: use united token events subscription --- config/groups/web3.ts | 2 + pages/api/rpc.ts | 3 + providers/lido-sdk.tsx | 8 +- providers/web3.tsx | 5 +- shared/hooks/use-balance.ts | 183 +++++++++++++++++++++++++++++------- utils/use-web3-transport.ts | 20 +++- 6 files changed, 180 insertions(+), 41 deletions(-) diff --git a/config/groups/web3.ts b/config/groups/web3.ts index 06f27f001..3e072920d 100644 --- a/config/groups/web3.ts +++ b/config/groups/web3.ts @@ -2,6 +2,8 @@ import { parseEther } from '@ethersproject/units'; // interval in ms for RPC event polling for token balance and tx updates export const PROVIDER_POLLING_INTERVAL = 12_000; +// how long in ms to wait for RPC batching(multicall and provider) +export const PROVIDER_BATCH_TIME = 150; // account for gas estimation // will always have >=0.001 ether, >=0.001 stETH, >=0.001 wstETH diff --git a/pages/api/rpc.ts b/pages/api/rpc.ts index 01eaa99d1..9e9daa157 100644 --- a/pages/api/rpc.ts +++ b/pages/api/rpc.ts @@ -43,6 +43,9 @@ const rpc = rpcFactory({ 'eth_getLogs', 'eth_chainId', 'net_version', + 'eth_newFilter', + 'eth_getFilterChanges', + 'eth_uninstallFilter', ], defaultChain: `${config.defaultChain}`, providers: { diff --git a/providers/lido-sdk.tsx b/providers/lido-sdk.tsx index b0c50e82a..ddd18a967 100644 --- a/providers/lido-sdk.tsx +++ b/providers/lido-sdk.tsx @@ -6,11 +6,13 @@ import { } from '@lidofinance/lido-ethereum-sdk/erc20'; import invariant from 'tiny-invariant'; import { useChainId, useClient, useConnectorClient } from 'wagmi'; +import { useTokenTransferSubscription } from 'shared/hooks/use-balance'; type LidoSDKContextValue = { core: LidoSDKCore; steth: LidoSDKstETH; wsteth: LidoSDKwstETH; + subscribeToTokenUpdates: ReturnType; }; const LidoSDKContext = createContext(null); @@ -23,9 +25,11 @@ export const useLidoSDK = () => { }; export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { + const subscribe = useTokenTransferSubscription(); const publicClient = useClient(); const chainId = useChainId(); const { data: walletClient } = useConnectorClient(); + const sdk = useMemo(() => { const core = new LidoSDKCore({ chainId, @@ -37,8 +41,8 @@ export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { const steth = new LidoSDKstETH({ core }); const wsteth = new LidoSDKwstETH({ core }); - return { core, steth, wsteth }; - }, [chainId, publicClient, walletClient]); + return { core, steth, wsteth, subscribeToTokenUpdates: subscribe }; + }, [chainId, publicClient, subscribe, walletClient]); return ( {children} ); diff --git a/providers/web3.tsx b/providers/web3.tsx index 92b3f6b48..5eeeccdd1 100644 --- a/providers/web3.tsx +++ b/providers/web3.tsx @@ -79,10 +79,11 @@ const Web3Provider: FC = ({ children }) => { chains: supportedChains, ssr: true, connectors: [], + batch: { - // eth_call's will be batched via multicall contract every 100ms + // eth_call's can be batched via multicall contract multicall: { - wait: 100, + wait: config.PROVIDER_BATCH_TIME, }, }, multiInjectedProviderDiscovery: false, diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index c3817301d..e1f9c9221 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { QueryKey, useQuery, useQueryClient } from '@tanstack/react-query'; import { BigNumber } from 'ethers'; import { useLidoSDK } from 'providers/lido-sdk'; -import { useEffect } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useBlockNumber, useBalance, @@ -12,14 +12,12 @@ import { } from 'wagmi'; import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; - import type { GetBalanceData } from 'wagmi/query'; -import { Address } from 'viem'; +import type { Address, Log } from 'viem'; -const dataToBN = (data: GetBalanceData) => - BigNumber.from(data.value.toString()); +const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); -const NativeToBN = (data: bigint) => BigNumber.from(data.toString()); +const balanceToBN = (data: GetBalanceData) => nativeToBN(data.value); export const useEthereumBalance = () => { const queryClient = useQueryClient(); @@ -27,7 +25,7 @@ export const useEthereumBalance = () => { const { data: blockNumber } = useBlockNumber({ watch: !!address }); const queryData = useBalance({ address, - query: { select: dataToBN, staleTime: 7000, enabled: !!address }, + query: { select: balanceToBN, staleTime: 7000, enabled: !!address }, }); useEffect(() => { @@ -46,47 +44,162 @@ type TokenContract = Awaited< ReturnType['getContract']> >; -const useTokenBalance = (contract: TokenContract, address?: Address) => { +type TokenSubscriptionState = Record< + Address, + { + subscribers: number; + queryKey: QueryKey; + } +>; + +type SubscribeArgs = { + address: Address; + queryKey: QueryKey; +}; + +export const Erc20EventsAbi = [ + { + type: 'event', + name: 'Approval', + inputs: [ + { indexed: true, name: 'owner', type: 'address' }, + { indexed: true, name: 'spender', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + }, + { + type: 'event', + name: 'Transfer', + inputs: [ + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + }, +] as const; + +export const useTokenTransferSubscription = () => { + const { address } = useAccount(); const queryClient = useQueryClient(); - const balanceQuery = useReadContract({ - abi: contract?.abi, - address: contract?.address, - functionName: 'balanceOf', - args: address && [address], - query: { enabled: !!address, select: NativeToBN }, - }); + const [subscriptions, setSubscriptions] = useState( + {}, + ); + + const tokens = useMemo( + () => Object.keys(subscriptions) as Address[], + [subscriptions], + ); + + const onLogs = useCallback( + (logs: Log[]) => { + for (const log of logs) { + const subscription = subscriptions[log.address]; + if (subscription) { + // we could optimistically update balance data + // but it's easier to refetch balance after transfer + void queryClient.invalidateQueries( + { + queryKey: subscription.queryKey, + }, + { cancelRefetch: false }, + ); + } + } + }, + [queryClient, subscriptions], + ); + + const shouldWatch = address && tokens.length > 0; useWatchContractEvent({ - abi: contract?.abi, - address: contract?.address, + abi: Erc20EventsAbi, eventName: 'Transfer', + batch: true, poll: true, - - enabled: !!(address && balanceQuery.data), - args: { from: address! }, - onLogs: () => { - void queryClient.invalidateQueries( - { queryKey: balanceQuery.queryKey }, - { cancelRefetch: false }, - ); + args: { + to: address, }, + address: tokens, + enabled: shouldWatch, + onLogs, }); useWatchContractEvent({ - abi: contract?.abi, - address: contract?.address, + abi: Erc20EventsAbi, eventName: 'Transfer', + batch: true, poll: true, - enabled: !!(address && balanceQuery.data), - args: { to: address! }, - onLogs: () => { - void queryClient.invalidateQueries( - { queryKey: balanceQuery.queryKey }, - { cancelRefetch: false }, - ); + args: { + from: address, }, + address: tokens, + enabled: shouldWatch, + onLogs, + }); + + const subscribe = useCallback(({ address, queryKey }: SubscribeArgs) => { + setSubscriptions((old) => { + const existing = old[address]; + return { + ...old, + [address]: { + queryKey, + subscribers: existing?.subscribers ?? 0 + 1, + }, + }; + }); + + // unsubscribe + return () => { + setSubscriptions((old) => { + const existing = old[address]; + if (existing) { + if (existing.subscribers > 1) { + return { + ...old, + [address]: { + ...existing, + subscribers: existing.subscribers - 1, + }, + }; + } else { + delete old[address]; + return { ...old }; + } + } else return old; + }); + }; + }, []); + + return subscribe; +}; + +// NB: contract can be undefined but for better wagmi typings is casted as NoNNullable +const useTokenBalance = (contract: TokenContract, address?: Address) => { + const { subscribeToTokenUpdates } = useLidoSDK(); + // const queryClient = useQueryClient(); + const balanceQuery = useReadContract({ + abi: contract?.abi, + address: contract?.address, + functionName: 'balanceOf', + args: address && [address], + query: { enabled: !!address, select: nativeToBN }, }); + useEffect(() => { + if (address && contract?.address) { + return subscribeToTokenUpdates({ + address: contract.address, + queryKey: balanceQuery.queryKey, + }); + } + }, [ + address, + contract?.address, + balanceQuery.queryKey, + subscribeToTokenUpdates, + ]); + return balanceQuery; }; diff --git a/utils/use-web3-transport.ts b/utils/use-web3-transport.ts index 2cd7761a9..0b887e328 100644 --- a/utils/use-web3-transport.ts +++ b/utils/use-web3-transport.ts @@ -1,5 +1,6 @@ // TODO: move this to dedicated web3 configuration module +import { config } from 'config'; import { useMemo, useCallback } from 'react'; import { Transport, @@ -12,6 +13,14 @@ import { } from 'viem'; import { Connection } from 'wagmi'; +// We disable those methods so wagmi uses getLogs intestead to watch events +// Filters are not suitable for public rpc and break between fallbacks +const DISABLED_METHODS = new Set([ + 'eth_newFilter', + 'eth_getFilterChanges', + 'eth_uninstallFilter', +]); + // Viem transport wrapper that allows runtime changes via setter const runtimeMutableTransport = ( mainTransports: Transport[], @@ -27,6 +36,10 @@ const runtimeMutableTransport = ( name: 'RuntimeMutableTransport', //@ts-expect-error invalid typings async request(requestParams, options) { + if (DISABLED_METHODS.has(requestParams.method)) + throw new Error( + `Method ${requestParams.method} is not supported`, + ); const transport = withInjectedTransport ? withInjectedTransport(params) : defaultTransport; @@ -62,10 +75,13 @@ export const useWeb3Transport = ( ({ transportMap, setTransportMap }, chain) => { const [transport, setTransport] = runtimeMutableTransport([ http(backendRpcMap[chain.id], { - batch: true, + batch: { wait: config.PROVIDER_BATCH_TIME }, name: backendRpcMap[chain.id], }), - http(), + http(undefined, { + batch: { wait: config.PROVIDER_BATCH_TIME }, + name: 'default HTTP RPC', + }), ]); return { transportMap: { From d0847d3726e081436fa0b0bacb3053e58229e28f Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 9 Aug 2024 19:36:09 +0700 Subject: [PATCH 15/40] feat: use approve --- .../withdrawals/hooks/contract/useRequest.ts | 2 +- .../wsteth/wrap/hooks/use-wrap-tx-approve.ts | 2 +- shared/hooks/use-allowance.ts | 118 ++++++++++++++++++ shared/hooks/use-balance.ts | 42 +++---- shared/hooks/useApprove.ts | 46 +++---- utils/use-web3-transport.ts | 33 +++-- 6 files changed, 177 insertions(+), 66 deletions(-) create mode 100644 shared/hooks/use-allowance.ts diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index 16a4cfba2..e77921856 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -224,7 +224,7 @@ export const useWithdrawalRequest = ({ approve, needsApprove, allowance, - initialLoading: loadingUseApprove, + isLoading: loadingUseApprove, } = useApprove( valueBN, tokenContract.address, diff --git a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts index ada13cc5e..c554be767 100644 --- a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts +++ b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts @@ -28,7 +28,7 @@ export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { approve: processApproveTx, needsApprove, allowance, - loading: isApprovalLoading, + isLoading: isApprovalLoading, } = useApprove( amount, stethTokenAddress, diff --git a/shared/hooks/use-allowance.ts b/shared/hooks/use-allowance.ts new file mode 100644 index 000000000..2111f11ac --- /dev/null +++ b/shared/hooks/use-allowance.ts @@ -0,0 +1,118 @@ +import { useQueryClient } from '@tanstack/react-query'; +import { BigNumber } from 'ethers'; +import { useCallback, useMemo } from 'react'; +import { Address } from 'viem'; +import { useReadContract, useWatchContractEvent } from 'wagmi'; + +const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); + +const Erc20AllowanceAbi = [ + { + type: 'event', + name: 'Approval', + inputs: [ + { indexed: true, name: 'owner', type: 'address' }, + { indexed: true, name: 'spender', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + }, + { + type: 'event', + name: 'Transfer', + inputs: [ + { indexed: true, name: 'from', type: 'address' }, + { indexed: true, name: 'to', type: 'address' }, + { indexed: false, name: 'value', type: 'uint256' }, + ], + }, + { + constant: true, + inputs: [ + { + name: '_owner', + type: 'address', + }, + { + name: '_spender', + type: 'address', + }, + ], + name: 'allowance', + outputs: [ + { + name: '', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, +] as const; + +type UseAllowanceProps = { + token: Address; + account: Address; + spender: Address; +}; + +export const useAllowance = ({ + token, + account, + spender, +}: UseAllowanceProps) => { + const queryClient = useQueryClient(); + const enabled = !!(token && account && spender); + + const allowanceQuery = useReadContract({ + abi: Erc20AllowanceAbi, + address: token, + functionName: 'allowance', + args: [account, spender], + query: { enabled, select: nativeToBN }, + }); + + const onLogs = useCallback(() => { + void queryClient.invalidateQueries( + { + queryKey: allowanceQuery.queryKey, + }, + { cancelRefetch: false }, + ); + }, [allowanceQuery.queryKey, queryClient]); + + useWatchContractEvent({ + abi: Erc20AllowanceAbi, + eventName: 'Approval', + batch: true, + poll: true, + args: useMemo( + () => ({ + owner: account, + spender, + }), + [account, spender], + ), + address: token, + enabled, + onLogs, + }); + + useWatchContractEvent({ + abi: Erc20AllowanceAbi, + eventName: 'Transfer', + batch: false, + poll: true, + args: useMemo( + () => ({ + from: account, + }), + [account], + ), + address: token, + enabled, + onLogs, + }); + + return allowanceQuery; +}; diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index e1f9c9221..6fbb73603 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -58,15 +58,6 @@ type SubscribeArgs = { }; export const Erc20EventsAbi = [ - { - type: 'event', - name: 'Approval', - inputs: [ - { indexed: true, name: 'owner', type: 'address' }, - { indexed: true, name: 'spender', type: 'address' }, - { indexed: false, name: 'value', type: 'uint256' }, - ], - }, { type: 'event', name: 'Transfer', @@ -114,11 +105,12 @@ export const useTokenTransferSubscription = () => { useWatchContractEvent({ abi: Erc20EventsAbi, eventName: 'Transfer', - batch: true, - poll: true, - args: { - to: address, - }, + args: useMemo( + () => ({ + to: address, + }), + [address], + ), address: tokens, enabled: shouldWatch, onLogs, @@ -127,11 +119,12 @@ export const useTokenTransferSubscription = () => { useWatchContractEvent({ abi: Erc20EventsAbi, eventName: 'Transfer', - batch: true, - poll: true, - args: { - from: address, - }, + args: useMemo( + () => ({ + from: address, + }), + [address], + ), address: tokens, enabled: shouldWatch, onLogs, @@ -177,7 +170,7 @@ export const useTokenTransferSubscription = () => { // NB: contract can be undefined but for better wagmi typings is casted as NoNNullable const useTokenBalance = (contract: TokenContract, address?: Address) => { const { subscribeToTokenUpdates } = useLidoSDK(); - // const queryClient = useQueryClient(); + const balanceQuery = useReadContract({ abi: contract?.abi, address: contract?.address, @@ -193,12 +186,9 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { queryKey: balanceQuery.queryKey, }); } - }, [ - address, - contract?.address, - balanceQuery.queryKey, - subscribeToTokenUpdates, - ]); + // queryKey causes rerender + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [address, contract?.address]); return balanceQuery; }; diff --git a/shared/hooks/useApprove.ts b/shared/hooks/useApprove.ts index 4c962f330..fc5960569 100644 --- a/shared/hooks/useApprove.ts +++ b/shared/hooks/useApprove.ts @@ -4,14 +4,15 @@ import { useCallback } from 'react'; import type { ContractReceipt } from '@ethersproject/contracts'; import { BigNumber } from '@ethersproject/bignumber'; import { getERC20Contract } from '@lido-sdk/contracts'; -import { useAllowance, useSDK } from '@lido-sdk/react'; +import { useSDK } from '@lido-sdk/react'; import { isContract } from 'utils/isContract'; import { runWithTransactionLogger } from 'utils'; import { useCurrentStaticRpcProvider } from './use-current-static-rpc-provider'; -import { STRATEGY_LAZY } from 'consts/swr-strategies'; import { sendTx } from 'utils/send-tx'; +import { useAllowance } from './use-allowance'; +import { Address } from 'viem'; type ApproveOptions = | { @@ -23,12 +24,9 @@ type ApproveOptions = export type UseApproveResponse = { approve: (options?: ApproveOptions) => Promise; + allowance: ReturnType['data']; needsApprove: boolean; - initialLoading: boolean; - allowance: BigNumber | undefined; - loading: boolean; - error: unknown; -}; +} & ReturnType; export const useApprove = ( amount: BigNumber, @@ -43,11 +41,14 @@ export const useApprove = ( invariant(token != null, 'Token is required'); invariant(spender != null, 'Spender is required'); - const result = useAllowance(token, spender, mergedOwner, STRATEGY_LAZY); - const { data: allowance, initialLoading, update: updateAllowance } = result; + const allowanceQuery = useAllowance({ + token: token as Address, + account: mergedOwner as Address, + spender: spender as Address, + }); const needsApprove = Boolean( - !initialLoading && allowance && !amount.isZero() && amount.gt(allowance), + allowanceQuery.data && !amount.isZero() && amount.gt(allowanceQuery.data), ); const approve = useCallback( @@ -86,39 +87,26 @@ export const useApprove = ( await onTxAwaited?.(receipt); } - await updateAllowance(); + await allowanceQuery.refetch(); return approveTxHash; }, [ + providerWeb3, chainId, account, token, - updateAllowance, + staticRpcProvider, + allowanceQuery, spender, amount, - staticRpcProvider, - providerWeb3, ], ); return { approve, needsApprove, - - allowance, - initialLoading, - - /* - * support dependency collection - * https://swr.vercel.app/advanced/performance#dependency-collection - */ - - get loading() { - return result.loading; - }, - get error() { - return result.error; - }, + allowance: allowanceQuery.data, + ...allowanceQuery, }; }; diff --git a/utils/use-web3-transport.ts b/utils/use-web3-transport.ts index 0b887e328..26a594d04 100644 --- a/utils/use-web3-transport.ts +++ b/utils/use-web3-transport.ts @@ -1,17 +1,17 @@ -// TODO: move this to dedicated web3 configuration module - import { config } from 'config'; import { useMemo, useCallback } from 'react'; import { - Transport, + type Transport, fallback, createTransport, http, EIP1193Provider, custom, Chain, + UnsupportedProviderMethodError, } from 'viem'; -import { Connection } from 'wagmi'; +import type { OnResponseFn } from 'viem/_types/clients/transports/fallback'; +import type { Connection } from 'wagmi'; // We disable those methods so wagmi uses getLogs intestead to watch events // Filters are not suitable for public rpc and break between fallbacks @@ -21,6 +21,8 @@ const DISABLED_METHODS = new Set([ 'eth_uninstallFilter', ]); +const NOOP = () => {}; + // Viem transport wrapper that allows runtime changes via setter const runtimeMutableTransport = ( mainTransports: Transport[], @@ -29,26 +31,39 @@ const runtimeMutableTransport = ( return [ (params) => { const defaultTransport = fallback(mainTransports)(params); - + let responseFn: OnResponseFn = NOOP; return createTransport( { key: 'RuntimeMutableTransport', name: 'RuntimeMutableTransport', //@ts-expect-error invalid typings async request(requestParams, options) { - if (DISABLED_METHODS.has(requestParams.method)) - throw new Error( - `Method ${requestParams.method} is not supported`, - ); const transport = withInjectedTransport ? withInjectedTransport(params) : defaultTransport; + + if (DISABLED_METHODS.has(requestParams.method)) { + const error = new UnsupportedProviderMethodError( + new Error(`Method ${requestParams.method} is not supported`), + ); + responseFn({ + error, + method: requestParams.method, + params: params as unknown[], + transport, + status: 'error', + }); + throw error; + } + + transport.value?.onResponse(responseFn); return transport.request(requestParams, options); }, type: 'fallback', }, { transports: defaultTransport.value?.transports, + onResponse: (fn: OnResponseFn) => (responseFn = fn), }, ); }, From fd438611d5bb5f34bf69ae9c5161783428a622d2 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 12 Aug 2024 14:27:11 +0700 Subject: [PATCH 16/40] fix: ipfs support --- providers/lido-sdk.tsx | 7 ++++++- shared/hooks/use-current-static-rpc-provider.ts | 7 ++++++- shared/hooks/use-mainnet-static-rpc-provider.ts | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/providers/lido-sdk.tsx b/providers/lido-sdk.tsx index ddd18a967..09ebaca09 100644 --- a/providers/lido-sdk.tsx +++ b/providers/lido-sdk.tsx @@ -7,6 +7,7 @@ import { import invariant from 'tiny-invariant'; import { useChainId, useClient, useConnectorClient } from 'wagmi'; import { useTokenTransferSubscription } from 'shared/hooks/use-balance'; +import { useGetRpcUrlByChainId } from 'config/rpc'; type LidoSDKContextValue = { core: LidoSDKCore; @@ -28,6 +29,8 @@ export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { const subscribe = useTokenTransferSubscription(); const publicClient = useClient(); const chainId = useChainId(); + const getRpcUrl = useGetRpcUrlByChainId(); + const fallbackRpcUrl = !publicClient ? getRpcUrl(chainId) : undefined; const { data: walletClient } = useConnectorClient(); const sdk = useMemo(() => { @@ -36,13 +39,15 @@ export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { logMode: 'none', rpcProvider: publicClient as any, web3Provider: walletClient as any, + // viem client can be unavailable on ipfs+dev first renders + rpcUrls: !publicClient && fallbackRpcUrl ? [fallbackRpcUrl] : undefined, }); const steth = new LidoSDKstETH({ core }); const wsteth = new LidoSDKwstETH({ core }); return { core, steth, wsteth, subscribeToTokenUpdates: subscribe }; - }, [chainId, publicClient, subscribe, walletClient]); + }, [chainId, fallbackRpcUrl, publicClient, subscribe, walletClient]); return ( {children} ); diff --git a/shared/hooks/use-current-static-rpc-provider.ts b/shared/hooks/use-current-static-rpc-provider.ts index d426728c1..31c82797f 100644 --- a/shared/hooks/use-current-static-rpc-provider.ts +++ b/shared/hooks/use-current-static-rpc-provider.ts @@ -5,6 +5,7 @@ import { getStaticRpcBatchProvider } from '@lido-sdk/providers'; import { StaticJsonRpcBatchProvider } from '@lidofinance/eth-providers'; import { useRpcUrl } from 'config/rpc'; +import { config } from 'config'; export const useCurrentStaticRpcProvider = (): { staticRpcProvider: StaticJsonRpcBatchProvider; @@ -14,7 +15,11 @@ export const useCurrentStaticRpcProvider = (): { const rpcUrl = useRpcUrl(); const staticRpcProvider = useMemo(() => { - return getStaticRpcBatchProvider(chainId, rpcUrl); + return getStaticRpcBatchProvider( + chainId, + rpcUrl, + config.PROVIDER_POLLING_INTERVAL, + ); }, [chainId, rpcUrl]); return { diff --git a/shared/hooks/use-mainnet-static-rpc-provider.ts b/shared/hooks/use-mainnet-static-rpc-provider.ts index 4da4fe368..674a67e59 100644 --- a/shared/hooks/use-mainnet-static-rpc-provider.ts +++ b/shared/hooks/use-mainnet-static-rpc-provider.ts @@ -4,10 +4,15 @@ import { StaticJsonRpcBatchProvider } from '@lidofinance/eth-providers'; import { useGetRpcUrlByChainId } from 'config/rpc'; import { CHAINS } from 'consts/chains'; +import { config } from 'config'; export const useMainnetStaticRpcProvider = (): StaticJsonRpcBatchProvider => { const getRpcUrl = useGetRpcUrlByChainId(); return useMemo(() => { - return getStaticRpcBatchProvider(1, getRpcUrl(CHAINS.Mainnet)); + return getStaticRpcBatchProvider( + CHAINS.Mainnet, + getRpcUrl(CHAINS.Mainnet), + config.PROVIDER_POLLING_INTERVAL, + ); }, [getRpcUrl]); }; From 1f369b6711e6a4c239efaad19cbb80a0fd0522a3 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 19 Aug 2024 18:51:51 +0700 Subject: [PATCH 17/40] fix: update ratelimit middleware --- package.json | 20 +++++------ yarn.lock | 95 ++++++++++++++++++++++++++++------------------------ 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 52a8523f8..a42ab097c 100644 --- a/package.json +++ b/package.json @@ -33,17 +33,17 @@ "@lido-sdk/helpers": "^1.6.0", "@lido-sdk/providers": "^1.4.15", "@lido-sdk/react": "^2.0.6", - "@lidofinance/analytics-matomo": "^0.41.0", - "@lidofinance/api-metrics": "^0.41.0", - "@lidofinance/api-rpc": "^0.41.0", - "@lidofinance/eth-api-providers": "^0.41.0", - "@lidofinance/eth-providers": "^0.41.0", + "@lidofinance/analytics-matomo": "^0.45.0", + "@lidofinance/api-metrics": "^0.45.0", + "@lidofinance/api-rpc": "^0.45.0", + "@lidofinance/eth-api-providers": "^0.45.0", + "@lidofinance/eth-providers": "^0.45.0", "@lidofinance/lido-ui": "^3.26.0", - "@lidofinance/next-api-wrapper": "^0.41.0", - "@lidofinance/next-ip-rate-limit": "^0.41.0", - "@lidofinance/next-pages": "^0.41.0", - "@lidofinance/rpc": "^0.41.0", - "@lidofinance/satanizer": "^0.41.0", + "@lidofinance/next-api-wrapper": "^0.45.0", + "@lidofinance/next-ip-rate-limit": "^0.45.0", + "@lidofinance/next-pages": "^0.45.0", + "@lidofinance/rpc": "^0.45.0", + "@lidofinance/satanizer": "^0.45.0", "@tanstack/react-query": "^5.48.0", "@wagmi/core": "^2.11.6", "bignumber.js": "9.1.0", diff --git a/yarn.lock b/yarn.lock index 9f1bb5b2a..4dac57a15 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1005,6 +1005,13 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== +"@babel/runtime@^7.12.5": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb" + integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.19.4", "@babel/runtime@^7.21.0": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" @@ -2161,20 +2168,20 @@ tiny-invariant "^1.1.0" tiny-warning "^1.0.3" -"@lidofinance/analytics-matomo@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/analytics-matomo/-/analytics-matomo-0.41.0.tgz#2cc20edba20ce21e0d745e2b44a54cd7acfd424e" - integrity sha512-8Z9n3BovqG6nmS8lUemJauiyq5EIK5TaPZdPQ3Dp5VcpVRqMekFhXeBf9srTqCrEPxnTwcBu09Mun59bgnNSoQ== +"@lidofinance/analytics-matomo@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/analytics-matomo/-/analytics-matomo-0.45.0.tgz#288c82e193679c2c2654c4e434ccab81f99b7335" + integrity sha512-f+NAMPUrHlnCDzLwBPgoNNf8xmwW3AMm7J3Vb/Y0as96H7gMFoqvpfyzRwOF3ov9/ZIieLKoFThTn/VFWHVwYg== -"@lidofinance/api-metrics@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/api-metrics/-/api-metrics-0.41.0.tgz#d7a85fe951031de00a3748de5aeb11c2c8533c9e" - integrity sha512-huIL210Lw0SzQqqTwjARXEbNhCj+0w8qbZQZVZRnIh6Zyra+cIQHRCLeBoUmyGeI3Usrjrn4wY5I8+gPQixRkA== +"@lidofinance/api-metrics@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/api-metrics/-/api-metrics-0.45.0.tgz#5991b49544a9477c3f14e59dc6d84daed46c96d1" + integrity sha512-CMoV2RFzexw4lsNTW0RTMo6aaJ58iq7uWtmGuF0JxyB4HFfr+8uDr1v6IdJSK+qjfQeATfOC8EnSJ2C5QKUcpg== -"@lidofinance/api-rpc@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/api-rpc/-/api-rpc-0.41.0.tgz#f62aed905c7b808583528e6d9377c9f2bc1ff271" - integrity sha512-CYQkNU5xgTN6tZlH3T3F6jgiZ5gKxWgmiGIBf9HwHzEfTdmOVETWQLdmKrpp0IUCZNOaSsT5fye02MdB5Kw+4Q== +"@lidofinance/api-rpc@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/api-rpc/-/api-rpc-0.45.0.tgz#0de2534c225c3a4271bb9cf55a228a17273aa222" + integrity sha512-t3UIuXt65nKQNP+e6thr6pAZ+8iKc7H/9vyD7PAhTuxNRMNj+PiVSZAErAjJjbo9/jbEHlgApsgpYwx4qAYIIA== "@lidofinance/eslint-config@^0.34.0": version "0.34.0" @@ -2183,15 +2190,15 @@ dependencies: typescript "^4.7" -"@lidofinance/eth-api-providers@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/eth-api-providers/-/eth-api-providers-0.41.0.tgz#29b0c8c7ff8b697f83d44d2439a9d4ad0aacdf26" - integrity sha512-9+8Uh3AcqwHobCcxkGvt9haflr2+0Ws8fThT/nTQA638oJXzMbOHdBYp/UEdwSAe6lMlhJnODwoO7eIF0gAT4Q== +"@lidofinance/eth-api-providers@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/eth-api-providers/-/eth-api-providers-0.45.0.tgz#99160808cba97fca6823fdc9d15d4299db509808" + integrity sha512-B6F/Y0i8L6EiZt1AJk1b4FYMm/CqlBJQzFjWpwegP03wNOh/l84BxQcM/i9PPenV7mm3+RNC/qb4h7v6/L5lEA== -"@lidofinance/eth-providers@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.41.0.tgz#31e8c4abbc22375ea0b1eb54a34eee72f175e906" - integrity sha512-qz/+C1J6soJjtrAOBIlfTdez60lK9lS8jqvdUSEGPHMr88Pg+6p77Zh5kzQynGXuwMeg7xKVqB5uMLFJPAOGGA== +"@lidofinance/eth-providers@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.45.0.tgz#8c49b4f3ae1316f3901fd8988ae8cd5918dbdfed" + integrity sha512-NAn+Eu0HL5cveC2gpl3fJLXRboKDJ0fO3ZTmgn94RuJEG4HoakXktaJUNnzFmLjUuHrhxUkqmIKtb9cT+C2bNg== "@lidofinance/lido-ui@^3.18.0": version "3.21.0" @@ -2221,34 +2228,34 @@ ua-parser-js "^1.0.35" use-callback-ref "1.2.5" -"@lidofinance/next-api-wrapper@^0.41.0", "@lidofinance/next-api-wrapper@~0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-api-wrapper/-/next-api-wrapper-0.41.0.tgz#994f22383032367d11ae4f2ea9ca9b5d15863a0f" - integrity sha512-ADo9OpuTddC4/e+EFnJExh/ztsdKYrpNEyVUuC4iciZ7oUndfA0/NXlBdKyC89L4cC72DCNtbAgemYpBv4wELQ== +"@lidofinance/next-api-wrapper@^0.45.0", "@lidofinance/next-api-wrapper@~0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/next-api-wrapper/-/next-api-wrapper-0.45.0.tgz#d97a225333393af9cf7bd71246ffd12e06b24b0c" + integrity sha512-vkpMRasRq3GWqK2QhlMWDKHPNSCFBbkqWZ9BSdeutWsfyHr1vgQ48u+bKQP5plkTviIFwFJ/Otw+qyCQcay3lA== -"@lidofinance/next-ip-rate-limit@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-ip-rate-limit/-/next-ip-rate-limit-0.41.0.tgz#1d37abb0c7a0fa533368733199122be9673c9cd6" - integrity sha512-HOJV+6dlwyuB+o7JbzCErcaeuUx7wSGWdTcfs1HyLr14YJb67kyVG7WmaSj1hbmpAE14ljSRL6UlS/WNIKjXcQ== +"@lidofinance/next-ip-rate-limit@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/next-ip-rate-limit/-/next-ip-rate-limit-0.45.0.tgz#09d53e4386d6d9f65541b141b9419a6139c15594" + integrity sha512-WoS91RrJhsgkWMM/YSGlG5hRnlOpy05j+bW/Z2zj+5V1968yzFg/nlYn7cJ8fdL1GC4PmG+KeKqnwIVdmTozWw== dependencies: - "@lidofinance/next-api-wrapper" "~0.41.0" + "@lidofinance/next-api-wrapper" "~0.45.0" -"@lidofinance/next-pages@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-pages/-/next-pages-0.41.0.tgz#d50cf36c4b3958b0fdab238f43b45ce95b21b5af" - integrity sha512-xZqMthO0dPCrmNGmkUi2ZDolzl0fKj0UtaZdg3cZPT0DdrqiYrAh6ut9CmFTEpa8IfqclDXUAdlzJfWt8TbKQA== +"@lidofinance/next-pages@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/next-pages/-/next-pages-0.45.0.tgz#45ec0441e74ced4c4f8d5b277c3f07b96513d653" + integrity sha512-64DvtGpSYSTscodFAem+gZ/1OvfkPrSNPV7Rbc3hY83vQj3lZ+DKhDBELv6pNOBh8KWvKSB2354vcNeuhKLuNg== -"@lidofinance/rpc@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/rpc/-/rpc-0.41.0.tgz#11f9ec0c9d45301939d90d07517a2d253f29d9c2" - integrity sha512-83ZqclCFMQ+5TBlMCbn+J03fspyvFZuCW6130bUdDSlqrV+WLxAFVcL+9lYv3NV6z7Ds3//6l35aRURLQiPdBg== +"@lidofinance/rpc@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/rpc/-/rpc-0.45.0.tgz#40fadc5d6d9716c0a7c76ec0754b56a1e438f46d" + integrity sha512-oNXgOvLTAPPn+3TyIhuLMLNCJC85AgCUL4G1dHf2KBmlCKVyQzx5tITKKIGJ2GIdPWxFtjqtT+kBMQ51v0Ed7w== dependencies: isomorphic-fetch "^3.0.0" -"@lidofinance/satanizer@^0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@lidofinance/satanizer/-/satanizer-0.41.0.tgz#005793a7d98c9a6cee97adcb4d6ff782ff5e8820" - integrity sha512-AK43IDg/HZBgnsk88RIPGE0WzJ81KtBYKvsWPjEAs2etTGpY4j3swUmJl218LBT3LqzLM5BZ0UrdFQpSW/GlYw== +"@lidofinance/satanizer@^0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@lidofinance/satanizer/-/satanizer-0.45.0.tgz#9bb379accbfc3a0eb70f0af85fb45e8d1519fba9" + integrity sha512-m0kcCQADxl7sl7CoWJWFgwpBOI19HHY38oDxVfPYGhfnT+lJN1N20bZe9YKTwAXXowh+uDSO+NCFy86I+P84IQ== "@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": version "1.1.2" @@ -4755,9 +4762,9 @@ camelize@^1.0.0: integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001580: - version "1.0.30001581" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" - integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== + version "1.0.30001651" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz" + integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== "cbw-sdk@npm:@coinbase/wallet-sdk@3.9.3": version "3.9.3" From e37dab32af1e7180153a9b5c278f025837eb003e Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 21 Aug 2024 13:41:07 +0700 Subject: [PATCH 18/40] chore: type import --- shared/hooks/useApprove.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared/hooks/useApprove.ts b/shared/hooks/useApprove.ts index fc5960569..0e1f2cc14 100644 --- a/shared/hooks/useApprove.ts +++ b/shared/hooks/useApprove.ts @@ -12,7 +12,8 @@ import { runWithTransactionLogger } from 'utils'; import { useCurrentStaticRpcProvider } from './use-current-static-rpc-provider'; import { sendTx } from 'utils/send-tx'; import { useAllowance } from './use-allowance'; -import { Address } from 'viem'; + +import type { Address } from 'viem'; type ApproveOptions = | { From 212546406cb787847aaa5c7ffd98174f1176b4f9 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Wed, 21 Aug 2024 15:58:19 +0700 Subject: [PATCH 19/40] fix: remove rpc methods --- pages/api/rpc.ts | 3 -- shared/hooks/use-balance.ts | 74 ++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/pages/api/rpc.ts b/pages/api/rpc.ts index 9e9daa157..01eaa99d1 100644 --- a/pages/api/rpc.ts +++ b/pages/api/rpc.ts @@ -43,9 +43,6 @@ const rpc = rpcFactory({ 'eth_getLogs', 'eth_chainId', 'net_version', - 'eth_newFilter', - 'eth_getFilterChanges', - 'eth_uninstallFilter', ], defaultChain: `${config.defaultChain}`, providers: { diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 6fbb73603..17bdd4681 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -1,4 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ + import { QueryKey, useQuery, useQueryClient } from '@tanstack/react-query'; import { BigNumber } from 'ethers'; import { useLidoSDK } from 'providers/lido-sdk'; @@ -53,7 +55,7 @@ type TokenSubscriptionState = Record< >; type SubscribeArgs = { - address: Address; + tokenAddress: Address; queryKey: QueryKey; }; @@ -85,16 +87,15 @@ export const useTokenTransferSubscription = () => { (logs: Log[]) => { for (const log of logs) { const subscription = subscriptions[log.address]; - if (subscription) { - // we could optimistically update balance data - // but it's easier to refetch balance after transfer - void queryClient.invalidateQueries( - { - queryKey: subscription.queryKey, - }, - { cancelRefetch: false }, - ); - } + if (!subscription) continue; + // we could optimistically update balance data + // but it's easier to refetch balance after transfer + void queryClient.invalidateQueries( + { + queryKey: subscription.queryKey, + }, + { cancelRefetch: false }, + ); } }, [queryClient, subscriptions], @@ -130,36 +131,35 @@ export const useTokenTransferSubscription = () => { onLogs, }); - const subscribe = useCallback(({ address, queryKey }: SubscribeArgs) => { + const subscribe = useCallback(({ tokenAddress, queryKey }: SubscribeArgs) => { setSubscriptions((old) => { - const existing = old[address]; + const existing = old[tokenAddress]; return { ...old, - [address]: { + [tokenAddress]: { queryKey, subscribers: existing?.subscribers ?? 0 + 1, }, }; }); - // unsubscribe + // returns unsubscribe to be used as useEffect return fn (for unmount) return () => { setSubscriptions((old) => { - const existing = old[address]; - if (existing) { - if (existing.subscribers > 1) { - return { - ...old, - [address]: { - ...existing, - subscribers: existing.subscribers - 1, - }, - }; - } else { - delete old[address]; - return { ...old }; - } - } else return old; + const existing = old[tokenAddress]; + if (!existing) return old; + if (existing.subscribers > 1) { + return { + ...old, + [tokenAddress]: { + ...existing, + subscribers: existing.subscribers - 1, + }, + }; + } else { + delete old[tokenAddress]; + return { ...old }; + } }); }; }, []); @@ -182,7 +182,7 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { useEffect(() => { if (address && contract?.address) { return subscribeToTokenUpdates({ - address: contract.address, + tokenAddress: contract.address, queryKey: balanceQuery.queryKey, }); } @@ -198,16 +198,14 @@ export const useStethBalance = () => { const { steth, core } = useLidoSDK(); - const { data: contractData, isLoading } = useQuery({ + const { data: contract, isLoading } = useQuery({ queryKey: ['steth-contract', core.chainId], enabled: !!address, staleTime: Infinity, queryFn: async () => steth.getContract(), }); - const contract = contractData as NonNullable; - - const balanceData = useTokenBalance(contract, address); + const balanceData = useTokenBalance(contract!, address); return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; }; @@ -217,16 +215,14 @@ export const useWstethBalance = () => { const { wsteth, core } = useLidoSDK(); - const { data: contractData, isLoading } = useQuery({ + const { data: contract, isLoading } = useQuery({ queryKey: ['wsteth-contract', core.chainId], enabled: !!address, staleTime: Infinity, queryFn: async () => wsteth.getContract(), }); - const contract = contractData as NonNullable; - - const balanceData = useTokenBalance(contract, address); + const balanceData = useTokenBalance(contract!, address); return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; }; From bc014aee4868110914d1029aad137d4b4d3548fc Mon Sep 17 00:00:00 2001 From: Dmitrii Podlesnyi Date: Wed, 21 Aug 2024 21:26:59 +0700 Subject: [PATCH 20/40] feat: rewards page matomo events --- consts/matomo-click-events.ts | 52 +++++++++++++++++++ features/rewards/components/export/Export.tsx | 3 ++ .../rewardsListHeader/LeftOptions.tsx | 22 ++++++-- .../rewardsListHeader/RightOptions.tsx | 17 +++++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/consts/matomo-click-events.ts b/consts/matomo-click-events.ts index b0e1f0b7c..92e5575bc 100644 --- a/consts/matomo-click-events.ts +++ b/consts/matomo-click-events.ts @@ -65,6 +65,16 @@ export const enum MATOMO_CLICK_EVENTS_TYPES { // /withdrawal/request and /withdrawal/claim shared events withdrawalWhatAreStakingPenaltiesFAQ = 'withdrawalWhatAreStakingPenaltiesFAQ', withdrawalNFTGuideFAQ = 'withdrawalNFTGuideFAQ', + + // /rewards page + rewardsExportCSV = 'rewardsExportCSV', + rewardsHistoricalStethPriceCheck = 'rewardsHistoricalStethPriceCheck', + rewardsHistoricalStethPriceUncheck = 'rewardsHistoricalStethPriceUncheck', + rewardsIncludeTransfersCheck = 'rewardsIncludeTransfersCheck', + rewardsIncludeTransfersUncheck = 'rewardsIncludeTransfersUncheck', + rewardsHistoricalCurrencyUSD = 'rewardsHistoricalCurrencyUSD', + rewardsHistoricalCurrencyEUR = 'rewardsHistoricalCurrencyEUR', + rewardsHistoricalCurrencyGBP = 'rewardsHistoricalCurrencyGBP', } export const MATOMO_CLICK_EVENTS: Record< @@ -351,4 +361,46 @@ export const MATOMO_CLICK_EVENTS: Record< 'Push on "How do I add the Lido NFT to my wallet" guide link in FAQ', 'eth_withdrawals_how_to_add_nft_guide_FAQ', ], + + // /rewards page + [MATOMO_CLICK_EVENTS_TYPES.rewardsExportCSV]: [ + 'Ethereum_Rewards_Widget', + 'Click on "Export CSV"', + 'eth_rewards_export_csv', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsHistoricalStethPriceCheck]: [ + 'Ethereum_Rewards_Widget', + 'Click check on "Historical stETH price" in check-box', + 'eth_historical_stETH_price_check_box_check', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsHistoricalStethPriceUncheck]: [ + 'Ethereum_Rewards_Widget', + 'Click uncheck on "Historical stETH price" in check-box', + 'eth_historical_stETH_price_check_box_uncheck', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsIncludeTransfersCheck]: [ + 'Ethereum_Rewards_Widget', + 'Click check on "Include transfers" in check-box', + 'eth_include_transfers_check_box_check', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsIncludeTransfersUncheck]: [ + 'Ethereum_Rewards_Widget', + 'Click uncheck on "Include transfers" in check-box', + 'eth_include_transfers_check_box_uncheck', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsHistoricalCurrencyUSD]: [ + 'Ethereum_Rewards_Widget', + 'Click on "USD" in currency choice', + 'eth_historical_usd_currency_choice', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsHistoricalCurrencyEUR]: [ + 'Ethereum_Rewards_Widget', + 'Click on "EUR" in currency choice', + 'eth_historical_eur_currency_choice', + ], + [MATOMO_CLICK_EVENTS_TYPES.rewardsHistoricalCurrencyGBP]: [ + 'Ethereum_Rewards_Widget', + 'Click on "GBP" in currency choice', + 'eth_historical_gbp_currency_choice', + ], }; diff --git a/features/rewards/components/export/Export.tsx b/features/rewards/components/export/Export.tsx index cb59ca184..3bea9230a 100644 --- a/features/rewards/components/export/Export.tsx +++ b/features/rewards/components/export/Export.tsx @@ -5,6 +5,8 @@ import { backendRequest } from 'features/rewards/fetchers/backend'; import { ButtonStyle } from './Exportstyled'; import type { CurrencyType } from 'features/rewards/constants'; +import { trackEvent } from '@lidofinance/analytics-matomo'; +import { MATOMO_CLICK_EVENTS } from 'consts/matomo-click-events'; type ExportProps = { currency: CurrencyType; @@ -33,6 +35,7 @@ export const Export = ({ }); const formatted = genExportData(currencyObject, result.events); saveAsCSV(formatted); + trackEvent(...MATOMO_CLICK_EVENTS.rewardsExportCSV); }; return ( diff --git a/features/rewards/components/rewardsListHeader/LeftOptions.tsx b/features/rewards/components/rewardsListHeader/LeftOptions.tsx index fef130268..fdb6e6bcc 100644 --- a/features/rewards/components/rewardsListHeader/LeftOptions.tsx +++ b/features/rewards/components/rewardsListHeader/LeftOptions.tsx @@ -3,6 +3,8 @@ import { Tooltip, Checkbox } from '@lidofinance/lido-ui'; import { useRewardsHistory } from 'features/rewards/hooks/useRewardsHistory'; import { LeftOptionsWrapper } from './styles'; +import { trackEvent } from '@lidofinance/analytics-matomo'; +import { MATOMO_CLICK_EVENTS } from 'consts/matomo-click-events'; export const LeftOptions: FC = () => { const { @@ -20,7 +22,14 @@ export const LeftOptions: FC = () => { > setIsIncludeTransfers(!isIncludeTransfers)} + onChange={() => { + trackEvent( + ...(!isIncludeTransfers + ? MATOMO_CLICK_EVENTS.rewardsIncludeTransfersCheck + : MATOMO_CLICK_EVENTS.rewardsIncludeTransfersUncheck), + ); + setIsIncludeTransfers(!isIncludeTransfers); + }} data-testid="includeTransfersCheckbox" label="Include transfers" /> @@ -33,9 +42,14 @@ export const LeftOptions: FC = () => { > - setIsUseArchiveExchangeRate(!isUseArchiveExchangeRate) - } + onChange={() => { + trackEvent( + ...(!isUseArchiveExchangeRate + ? MATOMO_CLICK_EVENTS.rewardsHistoricalStethPriceCheck + : MATOMO_CLICK_EVENTS.rewardsHistoricalStethPriceUncheck), + ); + setIsUseArchiveExchangeRate(!isUseArchiveExchangeRate); + }} data-testid="historicalStEthCheckbox" label="Historical stETH price" /> diff --git a/features/rewards/components/rewardsListHeader/RightOptions.tsx b/features/rewards/components/rewardsListHeader/RightOptions.tsx index 5b5632b6d..b323761c0 100644 --- a/features/rewards/components/rewardsListHeader/RightOptions.tsx +++ b/features/rewards/components/rewardsListHeader/RightOptions.tsx @@ -4,6 +4,14 @@ import { Export } from 'features/rewards/components/export'; import { RightOptionsWrapper } from './styles'; import { useRewardsHistory } from 'features/rewards/hooks/useRewardsHistory'; +import { MatomoEventType, trackEvent } from '@lidofinance/analytics-matomo'; +import { MATOMO_CLICK_EVENTS } from 'consts/matomo-click-events'; + +const MATOMO_EVENTS_MAP_CURRENCY_SELECTOR: Record = { + usd: MATOMO_CLICK_EVENTS.rewardsHistoricalCurrencyUSD, + eur: MATOMO_CLICK_EVENTS.rewardsHistoricalCurrencyEUR, + gbp: MATOMO_CLICK_EVENTS.rewardsHistoricalCurrencyGBP, +}; export const RightOptions: FC = () => { const { @@ -15,7 +23,14 @@ export const RightOptions: FC = () => { } = useRewardsHistory(); return ( - + { + const event = MATOMO_EVENTS_MAP_CURRENCY_SELECTOR[value]; + if (event) trackEvent(...event); + setCurrency(value); + }} + /> Date: Thu, 22 Aug 2024 15:57:38 +0700 Subject: [PATCH 21/40] fix: rewards balance for inputed address --- .../rewardsListContent/RewardsListContent.tsx | 8 +++- shared/hooks/use-balance.ts | 41 +++++++++++++++---- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/features/rewards/components/rewardsListContent/RewardsListContent.tsx b/features/rewards/components/rewardsListContent/RewardsListContent.tsx index bf496b83d..dac289fb5 100644 --- a/features/rewards/components/rewardsListContent/RewardsListContent.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListContent.tsx @@ -15,9 +15,12 @@ import { ErrorWrapper, } from './RewardsListContentStyles'; +import type { Address } from 'viem'; + export const RewardsListContent: FC = () => { const { error, + address, initialLoading, data, currencyObject, @@ -26,7 +29,10 @@ export const RewardsListContent: FC = () => { isLagging, } = useRewardsHistory(); const { data: stethBalance, isLoading: isStethBalanceLoading } = - useStethBalance(); + useStethBalance({ + account: address as Address, + shouldSubscribeToUpdates: false, + }); const hasSteth = stethBalance?.gt(Zero); if (!data && !initialLoading && !error) return ; diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 17bdd4681..9c0b7f5c0 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -59,6 +59,11 @@ type SubscribeArgs = { queryKey: QueryKey; }; +type UseBalanceProps = { + account?: Address; + shouldSubscribeToUpdates?: boolean; +}; + export const Erc20EventsAbi = [ { type: 'event', @@ -168,7 +173,11 @@ export const useTokenTransferSubscription = () => { }; // NB: contract can be undefined but for better wagmi typings is casted as NoNNullable -const useTokenBalance = (contract: TokenContract, address?: Address) => { +const useTokenBalance = ( + contract: TokenContract, + address?: Address, + shouldSubscribe = true, +) => { const { subscribeToTokenUpdates } = useLidoSDK(); const balanceQuery = useReadContract({ @@ -180,7 +189,7 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { }); useEffect(() => { - if (address && contract?.address) { + if (shouldSubscribe && address && contract?.address) { return subscribeToTokenUpdates({ tokenAddress: contract.address, queryKey: balanceQuery.queryKey, @@ -193,36 +202,52 @@ const useTokenBalance = (contract: TokenContract, address?: Address) => { return balanceQuery; }; -export const useStethBalance = () => { +export const useStethBalance = ({ + account, + shouldSubscribeToUpdates = true, +}: UseBalanceProps = {}) => { const { address } = useAccount(); + const mergedAccount = account ?? address; const { steth, core } = useLidoSDK(); const { data: contract, isLoading } = useQuery({ queryKey: ['steth-contract', core.chainId], - enabled: !!address, + enabled: !!mergedAccount, staleTime: Infinity, queryFn: async () => steth.getContract(), }); - const balanceData = useTokenBalance(contract!, address); + const balanceData = useTokenBalance( + contract!, + mergedAccount, + shouldSubscribeToUpdates, + ); return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; }; -export const useWstethBalance = () => { +export const useWstethBalance = ({ + account, + shouldSubscribeToUpdates = true, +}: UseBalanceProps = {}) => { const { address } = useAccount(); + const mergedAccount = account ?? address; const { wsteth, core } = useLidoSDK(); const { data: contract, isLoading } = useQuery({ queryKey: ['wsteth-contract', core.chainId], - enabled: !!address, + enabled: !!mergedAccount, staleTime: Infinity, queryFn: async () => wsteth.getContract(), }); - const balanceData = useTokenBalance(contract!, address); + const balanceData = useTokenBalance( + contract!, + mergedAccount, + shouldSubscribeToUpdates, + ); return { ...balanceData, isLoading: isLoading || balanceData.isLoading }; }; From 066b6f709fa0352d06290350b80082c93cba79b9 Mon Sep 17 00:00:00 2001 From: Dmitrii Podlesnyi Date: Thu, 22 Aug 2024 17:59:16 +0700 Subject: [PATCH 22/40] fix: do not send dexes rate request if tvl error occured --- features/withdrawals/hooks/useTvlError.ts | 26 ++++++++ features/withdrawals/hooks/useTvlMessage.ts | 16 +---- .../controls/token-amount-input-request.tsx | 10 +-- .../form/options/dex-options/dex-options.tsx | 61 +++++++++++++------ .../request/form/options/options-picker.tsx | 15 ++++- .../withdrawal-rates/use-withdrawal-rates.ts | 3 + 6 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 features/withdrawals/hooks/useTvlError.ts diff --git a/features/withdrawals/hooks/useTvlError.ts b/features/withdrawals/hooks/useTvlError.ts new file mode 100644 index 000000000..707ae2f21 --- /dev/null +++ b/features/withdrawals/hooks/useTvlError.ts @@ -0,0 +1,26 @@ +import { useMemo } from 'react'; +import { useController } from 'react-hook-form'; +import { + TvlErrorPayload, + ValidationTvlJoke, +} from '../request/request-form-context/validators'; +import { RequestFormInputType } from 'features/withdrawals/request/request-form-context'; + +const getTvlError = (error?: unknown) => + error && + typeof error === 'object' && + 'type' in error && + error.type == ValidationTvlJoke.type && + 'payload' in error + ? (error.payload as TvlErrorPayload) + : { balanceDiffSteth: undefined, tvlDiff: undefined }; + +export const useTvlError = () => { + const { + fieldState: { error }, + } = useController({ + name: 'amount', + }); + + return useMemo(() => getTvlError(error), [error]); +}; diff --git a/features/withdrawals/hooks/useTvlMessage.ts b/features/withdrawals/hooks/useTvlMessage.ts index f9e0c9804..485bc6b6d 100644 --- a/features/withdrawals/hooks/useTvlMessage.ts +++ b/features/withdrawals/hooks/useTvlMessage.ts @@ -2,10 +2,7 @@ import { useMemo } from 'react'; import { formatEther } from '@ethersproject/units'; import { shortenTokenValue } from 'utils'; -import { - TvlErrorPayload, - ValidationTvlJoke, -} from '../request/request-form-context/validators'; +import { useTvlError } from './useTvlError'; const texts: ((amount: string) => string)[] = [ (amount) => @@ -17,18 +14,11 @@ const texts: ((amount: string) => string)[] = [ const getText = () => texts[Math.floor(Math.random() * texts.length)]; -export const useTvlMessage = (error?: unknown) => { +export const useTvlMessage = () => { // To render one text per page before refresh const textTemplate = useMemo(() => getText(), []); - const { balanceDiffSteth, tvlDiff } = - error && - typeof error === 'object' && - 'type' in error && - error.type == ValidationTvlJoke.type && - 'payload' in error - ? (error.payload as TvlErrorPayload) - : { balanceDiffSteth: undefined, tvlDiff: undefined }; + const { balanceDiffSteth, tvlDiff } = useTvlError(); return { balanceDiff: balanceDiffSteth, diff --git a/features/withdrawals/request/form/controls/token-amount-input-request.tsx b/features/withdrawals/request/form/controls/token-amount-input-request.tsx index ab5e9b255..da15fffa6 100644 --- a/features/withdrawals/request/form/controls/token-amount-input-request.tsx +++ b/features/withdrawals/request/form/controls/token-amount-input-request.tsx @@ -1,4 +1,4 @@ -import { useController, useWatch } from 'react-hook-form'; +import { useWatch } from 'react-hook-form'; import { MATOMO_CLICK_EVENTS_TYPES } from 'consts/matomo-click-events'; import { InputDecoratorTvlStake } from 'features/withdrawals/shared/input-decorator-tvl-stake'; @@ -17,13 +17,7 @@ export const TokenAmountInputRequest = () => { const token = useWatch({ name: 'token' }); const { maxAmount, isTokenLocked } = useRequestFormData(); - const { - fieldState: { error }, - } = useController({ - name: 'amount', - }); - - const { balanceDiff } = useTvlMessage(error); + const { balanceDiff } = useTvlMessage(); return ( = (props) => { const [showMore, setShowMore] = useState(false); const [buttonText, setButtonText] = useState('See all options'); + + const { balanceDiffSteth } = useTvlError(); + const isPausedByTvlError = balanceDiffSteth !== undefined; + const { data, initialLoading, amount, selectedToken, enabledDexes } = - useWithdrawalRates(); + useWithdrawalRates({ + isPaused: isPausedByTvlError, + }); const isAnyDexEnabled = enabledDexes.length > 0; const allowExpand = enabledDexes.length > MAX_SHOWN_ELEMENTS; + const showLoader = !isPausedByTvlError && isAnyDexEnabled && initialLoading; + const showList = !isPausedByTvlError && isAnyDexEnabled && !initialLoading; + const showPausedList = isPausedByTvlError; + + const dexesListData = useMemo(() => { + if (showList) return data; + if (showPausedList) { + return enabledDexes.map((dexId) => ({ + ...getDexConfig(dexId), + toReceive: null, + rate: null, + })); + } + return null; + }, [data, enabledDexes, showList, showPausedList]); + return ( <> Aggregator's prices are not available now )} - {isAnyDexEnabled && - initialLoading && - enabledDexes.map((_, i) => )} - {isAnyDexEnabled && - !initialLoading && - data?.map(({ title, toReceive, link, rate, matomoEvent, icon }) => { - return ( - trackMatomoEvent(matomoEvent)} - url={link(amount, selectedToken)} - key={title} - toReceive={rate ? toReceive : null} - /> - ); - })} + {showLoader && enabledDexes.map((_, i) => )} + {(showList || showPausedList) && + dexesListData?.map( + ({ title, toReceive, link, rate, matomoEvent, icon }) => { + return ( + trackMatomoEvent(matomoEvent)} + url={link(amount, selectedToken)} + key={title} + toReceive={rate ? toReceive : null} + /> + ); + }, + )} {allowExpand && ( (Math.floor(num * 10000) / 10000).toString(); const DexButton: React.FC = ({ isActive, onClick }) => { + const { balanceDiffSteth } = useTvlError(); + const isPausedByTvlError = balanceDiffSteth !== undefined; const { initialLoading, bestRate, enabledDexes } = useWithdrawalRates({ + isPaused: isPausedByTvlError, fallbackValue: DEFAULT_VALUE_FOR_RATE, }); const isAnyDexEnabled = enabledDexes.length > 0; const bestRateValue = - bestRate && isAnyDexEnabled ? `1 : ${toFloor(bestRate)}` : '—'; + !isPausedByTvlError && bestRate && isAnyDexEnabled + ? `1 : ${toFloor(bestRate)}` + : '—'; + return ( = ({ isActive, onClick }) => { Best Rate: - {initialLoading ? : bestRateValue} + {initialLoading && !isPausedByTvlError ? ( + + ) : ( + bestRateValue + )} Waiting time:{' '} diff --git a/features/withdrawals/request/withdrawal-rates/use-withdrawal-rates.ts b/features/withdrawals/request/withdrawal-rates/use-withdrawal-rates.ts index 6c3a154e0..ced0afdc1 100644 --- a/features/withdrawals/request/withdrawal-rates/use-withdrawal-rates.ts +++ b/features/withdrawals/request/withdrawal-rates/use-withdrawal-rates.ts @@ -21,6 +21,7 @@ import type { export type useWithdrawalRatesOptions = { fallbackValue?: BigNumber; + isPaused?: boolean; }; const getWithdrawalRates = async ( @@ -53,6 +54,7 @@ const getWithdrawalRates = async ( export const useWithdrawalRates = ({ fallbackValue = Zero, + isPaused, }: useWithdrawalRatesOptions = {}) => { const [token, amount] = useWatch({ name: ['token', 'amount'], @@ -76,6 +78,7 @@ export const useWithdrawalRates = ({ { ...STRATEGY_LAZY, isPaused: () => + isPaused || !debouncedAmount || !debouncedAmount._isBigNumber || ENABLED_WITHDRAWAL_DEXES.length === 0, From d7a84aa1f4497fd819d5878eccad5d76fdf84622 Mon Sep 17 00:00:00 2001 From: Dmitrii Podlesnyi Date: Thu, 22 Aug 2024 18:16:04 +0700 Subject: [PATCH 23/40] fix: tvl error hook usage --- .../request/form/controls/input-group-request.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/features/withdrawals/request/form/controls/input-group-request.tsx b/features/withdrawals/request/form/controls/input-group-request.tsx index 5a5e7d516..290eb7421 100644 --- a/features/withdrawals/request/form/controls/input-group-request.tsx +++ b/features/withdrawals/request/form/controls/input-group-request.tsx @@ -1,14 +1,9 @@ import { FC, PropsWithChildren } from 'react'; import { useTvlMessage } from 'features/withdrawals/hooks'; -import { useFormState } from 'react-hook-form'; import { InputGroupHookForm } from 'shared/hook-form/controls/input-group-hook-form'; -import { RequestFormInputType } from '../../request-form-context'; export const InputGroupRequest: FC = ({ children }) => { - const { - errors: { amount: amountError }, - } = useFormState({ name: 'amount' }); - const { tvlMessage } = useTvlMessage(amountError); + const { tvlMessage } = useTvlMessage(); return ( {children} From 160c22d61f85a10efed092e8f6b1fec85fec3266 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 26 Aug 2024 17:10:02 +0700 Subject: [PATCH 24/40] feat: ledger live claim tooltip --- .../claim-form-context/claim-form-context.tsx | 21 ++++++++++----- .../use-max-selected-count.ts | 6 +++++ .../claim/form/requests-list/request-item.tsx | 27 ++++++++++++++++--- .../withdrawals-constants/index.ts | 1 + 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/features/withdrawals/claim/claim-form-context/claim-form-context.tsx b/features/withdrawals/claim/claim-form-context/claim-form-context.tsx index ac4c471b6..3b437b399 100644 --- a/features/withdrawals/claim/claim-form-context/claim-form-context.tsx +++ b/features/withdrawals/claim/claim-form-context/claim-form-context.tsx @@ -26,7 +26,9 @@ import { import { useClaimData } from 'features/withdrawals/contexts/claim-data-context'; import { useWeb3 } from 'reef-knot/web3-react'; -type ClaimFormDataContextValueType = ClaimFormHelperState; +type ClaimFormDataContextValueType = ClaimFormHelperState & { + maxSelectedCountReason: string | null; +}; const claimFormDataContext = createContext(null); @@ -42,8 +44,11 @@ export const ClaimFormProvider: FC = ({ children }) => { const { active } = useWeb3(); const { data } = useClaimData(); - const { maxSelectedRequestCount, defaultSelectedRequestCount } = - useMaxSelectedCount(); + const { + maxSelectedRequestCount, + defaultSelectedRequestCount, + maxSelectedCountReason, + } = useMaxSelectedCount(); const { getDefaultValues } = useGetDefaultValues(defaultSelectedRequestCount); const formObject = useForm({ @@ -54,10 +59,14 @@ export const ClaimFormProvider: FC = ({ children }) => { reValidateMode: 'onChange', }); const { watch, reset, setValue, getValues, formState } = formObject; - const claimFormDataContextValue = useHelperState( - watch, - maxSelectedRequestCount, + + const helperState = useHelperState(watch, maxSelectedRequestCount); + + const claimFormDataContextValue = useMemo( + () => ({ ...helperState, maxSelectedCountReason }), + [helperState, maxSelectedCountReason], ); + const { retryEvent, retryFire } = useFormControllerRetry(); const claim = useClaim({ onRetry: retryFire }); diff --git a/features/withdrawals/claim/claim-form-context/use-max-selected-count.ts b/features/withdrawals/claim/claim-form-context/use-max-selected-count.ts index caab735ad..d59b2cc3b 100644 --- a/features/withdrawals/claim/claim-form-context/use-max-selected-count.ts +++ b/features/withdrawals/claim/claim-form-context/use-max-selected-count.ts @@ -15,8 +15,14 @@ export const useMaxSelectedCount = () => { DEFAULT_CLAIM_REQUEST_SELECTED, maxSelectedRequestCount, ); + + const maxSelectedCountReason = isLedgerLive + ? 'Ledger Clear Sign allows to claim up to 2 requests per transaction' + : null; + return { maxSelectedRequestCount, defaultSelectedRequestCount, + maxSelectedCountReason, }; }; diff --git a/features/withdrawals/claim/form/requests-list/request-item.tsx b/features/withdrawals/claim/form/requests-list/request-item.tsx index fcf25521d..c3362aef4 100644 --- a/features/withdrawals/claim/form/requests-list/request-item.tsx +++ b/features/withdrawals/claim/form/requests-list/request-item.tsx @@ -2,7 +2,12 @@ import { forwardRef } from 'react'; import { useWeb3 } from 'reef-knot/web3-react'; import { useFormState, useWatch } from 'react-hook-form'; -import { Checkbox, CheckboxProps, External } from '@lidofinance/lido-ui'; +import { + Checkbox, + CheckboxProps, + External, + Tooltip, +} from '@lidofinance/lido-ui'; import { FormatToken } from 'shared/formatters'; import { RequestStatus } from './request-item-status'; @@ -21,7 +26,7 @@ export const RequestItem = forwardRef( ({ token_id, name, disabled, index, ...props }, ref) => { const { chainId } = useWeb3(); const { isSubmitting } = useFormState(); - const { canSelectMore } = useClaimFormData(); + const { canSelectMore, maxSelectedCountReason } = useClaimFormData(); const { checked, status } = useWatch< ClaimFormInputType, `requests.${number}` @@ -42,15 +47,23 @@ export const RequestItem = forwardRef( : status.amountOfStETH; const symbol = isClaimable ? 'ETH' : 'stETH'; + const showDisabledReasonTooltip = + maxSelectedCountReason && + !canSelectMore && + status.isFinalized && + !checked; + const label = ( ); - return ( + const requestBody = ( ( ); + + return showDisabledReasonTooltip ? ( + + {requestBody} + + ) : ( + requestBody + ); }, ); diff --git a/features/withdrawals/withdrawals-constants/index.ts b/features/withdrawals/withdrawals-constants/index.ts index 1fcc9bbdc..b640c2317 100644 --- a/features/withdrawals/withdrawals-constants/index.ts +++ b/features/withdrawals/withdrawals-constants/index.ts @@ -3,6 +3,7 @@ import type { DexWithdrawalApi } from '../request/withdrawal-rates'; // max requests count for one tx export const MAX_REQUESTS_COUNT = 256; +// Leger Clear Sign only allows 2 requests per claim export const MAX_REQUESTS_COUNT_LEDGER_LIMIT = 2; export const DEFAULT_CLAIM_REQUEST_SELECTED = 80; From 027451c8fd46c080504edc9b690309388b70b978 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 30 Aug 2024 13:52:28 +0700 Subject: [PATCH 25/40] fix: event subscription --- shared/hooks/use-balance.ts | 77 +++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 9c0b7f5c0..56ebdade8 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -15,7 +15,7 @@ import { import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; import type { GetBalanceData } from 'wagmi/query'; -import type { Address, Log } from 'viem'; +import type { Address, WatchContractEventOnLogsFn } from 'viem'; const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); @@ -76,6 +76,12 @@ export const Erc20EventsAbi = [ }, ] as const; +type OnLogsFn = WatchContractEventOnLogsFn< + typeof Erc20EventsAbi, + 'Transfer', + true +>; + export const useTokenTransferSubscription = () => { const { address } = useAccount(); const queryClient = useQueryClient(); @@ -88,10 +94,11 @@ export const useTokenTransferSubscription = () => { [subscriptions], ); - const onLogs = useCallback( - (logs: Log[]) => { + const onLogs: OnLogsFn = useCallback( + (logs) => { for (const log of logs) { - const subscription = subscriptions[log.address]; + const subscription = + subscriptions[log.address.toLowerCase() as Address]; if (!subscription) continue; // we could optimistically update balance data // but it's easier to refetch balance after transfer @@ -136,38 +143,42 @@ export const useTokenTransferSubscription = () => { onLogs, }); - const subscribe = useCallback(({ tokenAddress, queryKey }: SubscribeArgs) => { - setSubscriptions((old) => { - const existing = old[tokenAddress]; - return { - ...old, - [tokenAddress]: { - queryKey, - subscribers: existing?.subscribers ?? 0 + 1, - }, - }; - }); - - // returns unsubscribe to be used as useEffect return fn (for unmount) - return () => { + const subscribe = useCallback( + ({ tokenAddress: _tokenAddress, queryKey }: SubscribeArgs) => { + const tokenAddress = _tokenAddress.toLowerCase() as Address; setSubscriptions((old) => { const existing = old[tokenAddress]; - if (!existing) return old; - if (existing.subscribers > 1) { - return { - ...old, - [tokenAddress]: { - ...existing, - subscribers: existing.subscribers - 1, - }, - }; - } else { - delete old[tokenAddress]; - return { ...old }; - } + return { + ...old, + [tokenAddress]: { + queryKey, + subscribers: existing?.subscribers ?? 0 + 1, + }, + }; }); - }; - }, []); + + // returns unsubscribe to be used as useEffect return fn (for unmount) + return () => { + setSubscriptions((old) => { + const existing = old[tokenAddress]; + if (!existing) return old; + if (existing.subscribers > 1) { + return { + ...old, + [tokenAddress]: { + ...existing, + subscribers: existing.subscribers - 1, + }, + }; + } else { + delete old[tokenAddress]; + return { ...old }; + } + }); + }; + }, + [], + ); return subscribe; }; From c6a0730b730426b3756648d2cdbad063f6dcaeec Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 30 Aug 2024 15:30:29 +0700 Subject: [PATCH 26/40] fix: await tx with wagmi --- config/groups/web3.ts | 2 +- features/stake/stake-form/use-stake.ts | 16 +++-- .../withdrawals/hooks/contract/useClaim.ts | 9 ++- .../withdrawals/hooks/contract/useRequest.ts | 26 ++++----- .../hooks/use-unwrap-form-processing.ts | 58 ++++++++++++++----- .../unwrap/hooks/use-unwrap-tx-processing.ts | 38 ------------ .../wrap/hooks/use-wrap-form-processing.ts | 31 +++++++--- shared/hooks/use-tx-conformation.ts | 19 ++++++ shared/hooks/useApprove.ts | 14 ++--- 9 files changed, 116 insertions(+), 97 deletions(-) delete mode 100644 features/wsteth/unwrap/hooks/use-unwrap-tx-processing.ts create mode 100644 shared/hooks/use-tx-conformation.ts diff --git a/config/groups/web3.ts b/config/groups/web3.ts index 3e072920d..0cbb31961 100644 --- a/config/groups/web3.ts +++ b/config/groups/web3.ts @@ -1,7 +1,7 @@ import { parseEther } from '@ethersproject/units'; // interval in ms for RPC event polling for token balance and tx updates -export const PROVIDER_POLLING_INTERVAL = 12_000; +export const PROVIDER_POLLING_INTERVAL = 7_000; // how long in ms to wait for RPC batching(multicall and provider) export const PROVIDER_BATCH_TIME = 150; diff --git a/features/stake/stake-form/use-stake.ts b/features/stake/stake-form/use-stake.ts index a772a63f5..2d9646661 100644 --- a/features/stake/stake-form/use-stake.ts +++ b/features/stake/stake-form/use-stake.ts @@ -18,6 +18,7 @@ import { MockLimitReachedError, getAddress } from './utils'; import { useTxModalStagesStake } from './hooks/use-tx-modal-stages-stake'; import { sendTx } from 'utils/send-tx'; +import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; type StakeArguments = { amount: BigNumber | null; @@ -32,16 +33,16 @@ type StakeOptions = { export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { const stethContractWeb3 = useSTETHContractWeb3(); const stethContract = useSTETHContractRPC(); - const { account, chainId } = useWeb3(); + const { account } = useWeb3(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { providerWeb3 } = useSDK(); const { txModalStages } = useTxModalStagesStake(); + const waitForTx = useTxConfirmation(); return useCallback( async ({ amount, referral }: StakeArguments): Promise => { try { invariant(amount, 'amount is null'); - invariant(chainId, 'chainId is not defined'); invariant(account, 'account is not defined'); invariant(providerWeb3, 'providerWeb3 not defined'); invariant(stethContractWeb3, 'steth is not defined'); @@ -92,14 +93,11 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { txModalStages.pending(amount, txHash); - if (!isMultisig) { - await runWithTransactionLogger('Stake block confirmation', () => - staticRpcProvider.waitForTransaction(txHash), - ); - } + await runWithTransactionLogger('Stake block confirmation', () => + waitForTx(txHash), + ); const stethBalance = await stethContract.balanceOf(account); - await onConfirm?.(); txModalStages.success(stethBalance, txHash); @@ -112,7 +110,6 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { } }, [ - chainId, account, providerWeb3, stethContractWeb3, @@ -120,6 +117,7 @@ export const useStake = ({ onConfirm, onRetry }: StakeOptions) => { staticRpcProvider, stethContract, onConfirm, + waitForTx, onRetry, ], ); diff --git a/features/withdrawals/hooks/contract/useClaim.ts b/features/withdrawals/hooks/contract/useClaim.ts index 0e55e9bcf..6051f0c76 100644 --- a/features/withdrawals/hooks/contract/useClaim.ts +++ b/features/withdrawals/hooks/contract/useClaim.ts @@ -13,6 +13,7 @@ import { useSDK } from '@lido-sdk/react'; import { useTxModalStagesClaim } from 'features/withdrawals/claim/transaction-modal-claim/use-tx-modal-stages-claim'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; import { sendTx } from 'utils/send-tx'; +import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; type Args = { onRetry?: () => void; @@ -25,6 +26,7 @@ export const useClaim = ({ onRetry }: Args) => { const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { optimisticClaimRequests } = useClaimData(); const { txModalStages } = useTxModalStagesClaim(); + const waitForTx = useTxConfirmation(); return useCallback( async (sortedRequests: RequestStatusClaimable[]) => { @@ -70,8 +72,8 @@ export const useClaim = ({ onRetry }: Args) => { txModalStages.pending(amount, txHash); - await runWithTransactionLogger('Claim block confirmation', async () => - staticRpcProvider.waitForTransaction(txHash), + await runWithTransactionLogger('Claim block confirmation', () => + waitForTx(txHash), ); await optimisticClaimRequests(sortedRequests); @@ -89,8 +91,9 @@ export const useClaim = ({ onRetry }: Args) => { account, providerWeb3, txModalStages, - staticRpcProvider, optimisticClaimRequests, + staticRpcProvider, + waitForTx, onRetry, ], ); diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index e77921856..3f234e400 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -29,12 +29,13 @@ import { useTxModalStagesRequest } from 'features/withdrawals/request/transactio import { useTransactionModal } from 'shared/transaction-modal/transaction-modal'; import { sendTx } from 'utils/send-tx'; import { overrideWithQAMockBoolean } from 'utils/qa'; +import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; // this encapsulates permit/approval & steth/wsteth flows const useWithdrawalRequestMethods = () => { const { providerWeb3 } = useSDK(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); - const { account, chainId, contractWeb3 } = useWithdrawalsContract(); + const { account, contractWeb3 } = useWithdrawalsContract(); const permitSteth = useCallback( async ({ @@ -44,8 +45,6 @@ const useWithdrawalRequestMethods = () => { signature?: GatherPermitSignatureResult; requests: BigNumber[]; }) => { - invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); invariant(providerWeb3, 'must have providerWeb3'); invariant(signature, 'must have signature'); invariant(contractWeb3, 'must have contractWeb3'); @@ -73,7 +72,7 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, providerWeb3, staticRpcProvider], + [contractWeb3, providerWeb3, staticRpcProvider], ); const permitWsteth = useCallback( @@ -84,8 +83,6 @@ const useWithdrawalRequestMethods = () => { signature?: GatherPermitSignatureResult; requests: BigNumber[]; }) => { - invariant(chainId, 'must have chainId'); - invariant(account, 'must have account'); invariant(signature, 'must have signature'); invariant(providerWeb3, 'must have providerWeb3'); invariant(contractWeb3, 'must have contractWeb3'); @@ -113,12 +110,11 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, providerWeb3, staticRpcProvider], + [contractWeb3, providerWeb3, staticRpcProvider], ); const steth = useCallback( async ({ requests }: { requests: BigNumber[] }) => { - invariant(chainId, 'must have chainId'); invariant(account, 'must have account'); invariant(contractWeb3, 'must have contractWeb3'); invariant(providerWeb3, 'must have providerWeb3'); @@ -140,12 +136,11 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider, providerWeb3], + [account, contractWeb3, staticRpcProvider, providerWeb3], ); const wstETH = useCallback( async ({ requests }: { requests: BigNumber[] }) => { - invariant(chainId, 'must have chainId'); invariant(account, 'must have account'); invariant(contractWeb3, 'must have contractWeb3'); invariant(providerWeb3, 'must have providerWeb3'); @@ -167,7 +162,7 @@ const useWithdrawalRequestMethods = () => { return callback; }, - [account, chainId, contractWeb3, staticRpcProvider, providerWeb3], + [account, contractWeb3, staticRpcProvider, providerWeb3], ); return useCallback( @@ -202,7 +197,6 @@ export const useWithdrawalRequest = ({ }: useWithdrawalRequestParams) => { const { chainId } = useSDK(); const withdrawalQueueAddress = getWithdrawalQueueAddress(chainId); - const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { connector } = useAccount(); const { account } = useWeb3(); @@ -210,6 +204,7 @@ export const useWithdrawalRequest = ({ const { txModalStages } = useTxModalStagesRequest(); const getRequestMethod = useWithdrawalRequestMethods(); const { isMultisig, isLoading: isMultisigLoading } = useIsMultisig(); + const waitForTx = useTxConfirmation(); const wstethContract = useWSTETHContractRPC(); const stethContract = useSTETHContractRPC(); @@ -322,8 +317,9 @@ export const useWithdrawalRequest = ({ txModalStages.pending(amount, token, txHash); if (!isMultisig) { - await runWithTransactionLogger('Stake block confirmation', () => - staticRpcProvider.waitForTransaction(txHash), + await runWithTransactionLogger( + 'Withdrawal Request block confirmation', + () => waitForTx(txHash), ); } @@ -347,8 +343,8 @@ export const useWithdrawalRequest = ({ needsApprove, onConfirm, onRetry, - staticRpcProvider, txModalStages, + waitForTx, ], ); diff --git a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts index 21450e3f5..f7fec3819 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts @@ -1,18 +1,24 @@ import invariant from 'tiny-invariant'; import { useCallback } from 'react'; -import { useSTETHContractRPC, useWSTETHContractRPC } from '@lido-sdk/react'; +import { + useSDK, + useSTETHContractRPC, + useWSTETHContractRPC, + useWSTETHContractWeb3, +} from '@lido-sdk/react'; import { useWeb3 } from 'reef-knot/web3-react'; -import { useUnwrapTxProcessing } from './use-unwrap-tx-processing'; import { useTxModalStagesUnwrap } from './use-tx-modal-stages-unwrap'; import { isContract } from 'utils/isContract'; import { runWithTransactionLogger } from 'utils'; import type { UnwrapFormInputType } from '../unwrap-form-context'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; +import { sendTx } from 'utils/send-tx'; +import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; type UseUnwrapFormProcessorArgs = { - onConfirm?: () => Promise; + onConfirm: () => Promise; onRetry?: () => void; }; @@ -21,24 +27,42 @@ export const useUnwrapFormProcessor = ({ onRetry, }: UseUnwrapFormProcessorArgs) => { const { account } = useWeb3(); + const { providerWeb3 } = useSDK(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); - const processWrapTx = useUnwrapTxProcessing(); + const { txModalStages } = useTxModalStagesUnwrap(); const stETHContractRPC = useSTETHContractRPC(); const wstETHContractRPC = useWSTETHContractRPC(); - const { txModalStages } = useTxModalStagesUnwrap(); + const wstethContractWeb3 = useWSTETHContractWeb3(); + const waitForTx = useTxConfirmation(); return useCallback( async ({ amount }: UnwrapFormInputType) => { try { invariant(amount, 'amount should be presented'); invariant(account, 'address should be presented'); - const isMultisig = await isContract(account, staticRpcProvider); - const willReceive = await wstETHContractRPC.getStETHByWstETH(amount); + invariant(providerWeb3, 'providerWeb3 must be presented'); + invariant(wstethContractWeb3, 'must have wstethContractWeb3'); + + const [isMultisig, willReceive] = await Promise.all([ + isContract(account, staticRpcProvider), + wstETHContractRPC.getStETHByWstETH(amount), + ]); txModalStages.sign(amount, willReceive); - const txHash = await runWithTransactionLogger('Unwrap signing', () => - processWrapTx({ amount, isMultisig }), + const txHash = await runWithTransactionLogger( + 'Unwrap signing', + async () => { + const tx = + await wstethContractWeb3.populateTransaction.unwrap(amount); + + return sendTx({ + tx, + isMultisig, + staticProvider: staticRpcProvider, + walletProvider: providerWeb3, + }); + }, ); if (isMultisig) { @@ -48,13 +72,15 @@ export const useUnwrapFormProcessor = ({ txModalStages.pending(amount, willReceive, txHash); - await runWithTransactionLogger('Unwrap block confirmation', async () => - staticRpcProvider.waitForTransaction(txHash), + await runWithTransactionLogger('Unwrap block confirmation', () => + waitForTx(txHash), ); - const stethBalance = await stETHContractRPC.balanceOf(account); + const [stethBalance] = await Promise.all([ + stETHContractRPC.balanceOf(account), + onConfirm(), + ]); - await onConfirm?.(); txModalStages.success(stethBalance, txHash); return true; } catch (error: any) { @@ -65,12 +91,14 @@ export const useUnwrapFormProcessor = ({ }, [ account, + providerWeb3, + wstethContractWeb3, + staticRpcProvider, wstETHContractRPC, txModalStages, stETHContractRPC, onConfirm, - processWrapTx, - staticRpcProvider, + waitForTx, onRetry, ], ); diff --git a/features/wsteth/unwrap/hooks/use-unwrap-tx-processing.ts b/features/wsteth/unwrap/hooks/use-unwrap-tx-processing.ts deleted file mode 100644 index eb0742eed..000000000 --- a/features/wsteth/unwrap/hooks/use-unwrap-tx-processing.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useCallback } from 'react'; -import invariant from 'tiny-invariant'; - -import { useSDK, useWSTETHContractWeb3 } from '@lido-sdk/react'; - -import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; - -import type { UnwrapFormInputType } from '../unwrap-form-context'; -import { sendTx } from 'utils/send-tx'; - -type UnwrapTxProcessorArgs = Omit & { - isMultisig: boolean; -}; - -export const useUnwrapTxProcessing = () => { - const { chainId, providerWeb3 } = useSDK(); - const { staticRpcProvider } = useCurrentStaticRpcProvider(); - const wstethContractWeb3 = useWSTETHContractWeb3(); - - return useCallback( - async ({ isMultisig, amount }: UnwrapTxProcessorArgs) => { - invariant(amount, 'amount id must be presented'); - invariant(chainId, 'chain id must be presented'); - invariant(providerWeb3, 'providerWeb3 must be presented'); - invariant(wstethContractWeb3, 'must have wstethContractWeb3'); - - const tx = await wstethContractWeb3.populateTransaction.unwrap(amount); - - return sendTx({ - tx, - isMultisig, - staticProvider: staticRpcProvider, - walletProvider: providerWeb3, - }); - }, - [chainId, providerWeb3, staticRpcProvider, wstethContractWeb3], - ); -}; diff --git a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts index 3c5daa786..cbccc544e 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts @@ -3,20 +3,23 @@ import invariant from 'tiny-invariant'; import { useCallback } from 'react'; import { useWeb3 } from 'reef-knot/web3-react'; import { useWrapTxProcessing } from './use-wrap-tx-processing'; + import { useTxModalWrap } from './use-tx-modal-stages-wrap'; -import { useWSTETHContractRPC } from '@lido-sdk/react'; +import { useSDK, useWSTETHContractRPC } from '@lido-sdk/react'; import { runWithTransactionLogger } from 'utils'; import { isContract } from 'utils/isContract'; +import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; +import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; + import type { WrapFormApprovalData, WrapFormInputType, } from '../wrap-form-context'; -import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; type UseWrapFormProcessorArgs = { approvalData: WrapFormApprovalData; - onConfirm?: () => Promise; + onConfirm: () => Promise; onRetry?: () => void; }; @@ -30,15 +33,21 @@ export const useWrapFormProcessor = ({ const { isApprovalNeededBeforeWrap, processApproveTx } = approvalData; const { txModalStages } = useTxModalWrap(); const wstETHContractRPC = useWSTETHContractRPC(); + const waitForTx = useTxConfirmation(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); + const { providerWeb3 } = useSDK(); return useCallback( async ({ amount, token }: WrapFormInputType) => { try { invariant(amount, 'amount should be presented'); invariant(account, 'address should be presented'); - const isMultisig = await isContract(account, staticRpcProvider); - const willReceive = await wstETHContractRPC.getWstETHByStETH(amount); + invariant(providerWeb3, 'providerWeb3 should be presented'); + + const [isMultisig, willReceive] = await Promise.all([ + isContract(account, staticRpcProvider), + wstETHContractRPC.getWstETHByStETH(amount), + ]); if (isApprovalNeededBeforeWrap) { txModalStages.signApproval(amount, token); @@ -70,12 +79,14 @@ export const useWrapFormProcessor = ({ txModalStages.pending(amount, token, willReceive, txHash); await runWithTransactionLogger('Wrap block confirmation', () => - staticRpcProvider.waitForTransaction(txHash), + waitForTx(txHash), ); - const wstethBalance = await wstETHContractRPC.balanceOf(account); + const [wstethBalance] = await Promise.all([ + wstETHContractRPC.balanceOf(account), + onConfirm(), + ]); - await onConfirm?.(); txModalStages.success(wstethBalance, txHash); return true; } catch (error) { @@ -86,13 +97,15 @@ export const useWrapFormProcessor = ({ }, [ account, + providerWeb3, + staticRpcProvider, wstETHContractRPC, isApprovalNeededBeforeWrap, txModalStages, onConfirm, processApproveTx, processWrapTx, - staticRpcProvider, + waitForTx, onRetry, ], ); diff --git a/shared/hooks/use-tx-conformation.ts b/shared/hooks/use-tx-conformation.ts new file mode 100644 index 000000000..7560046ef --- /dev/null +++ b/shared/hooks/use-tx-conformation.ts @@ -0,0 +1,19 @@ +import { useCallback } from 'react'; +import type { Hash } from 'viem'; +import { waitForTransactionReceipt } from 'viem/actions'; +import { useClient } from 'wagmi'; + +// helper hook until migration to wagmi is complete +// awaits TX trough wagmi transport to allow sync with balance hooks +export const useTxConfirmation = () => { + const client = useClient(); + return useCallback( + (hash: string) => { + return waitForTransactionReceipt(client as any, { + confirmations: 1, + hash: hash as Hash, + }); + }, + [client], + ); +}; diff --git a/shared/hooks/useApprove.ts b/shared/hooks/useApprove.ts index 0e1f2cc14..3d26c6043 100644 --- a/shared/hooks/useApprove.ts +++ b/shared/hooks/useApprove.ts @@ -1,7 +1,6 @@ import invariant from 'tiny-invariant'; import { useCallback } from 'react'; -import type { ContractReceipt } from '@ethersproject/contracts'; import { BigNumber } from '@ethersproject/bignumber'; import { getERC20Contract } from '@lido-sdk/contracts'; import { useSDK } from '@lido-sdk/react'; @@ -12,14 +11,15 @@ import { runWithTransactionLogger } from 'utils'; import { useCurrentStaticRpcProvider } from './use-current-static-rpc-provider'; import { sendTx } from 'utils/send-tx'; import { useAllowance } from './use-allowance'; +import { useTxConfirmation } from './use-tx-conformation'; -import type { Address } from 'viem'; +import type { Address, TransactionReceipt } from 'viem'; type ApproveOptions = | { onTxStart?: () => void | Promise; onTxSent?: (tx: string) => void | Promise; - onTxAwaited?: (tx: ContractReceipt) => void | Promise; + onTxAwaited?: (tx: TransactionReceipt) => void | Promise; } | undefined; @@ -35,8 +35,9 @@ export const useApprove = ( spender: string, owner?: string, ): UseApproveResponse => { - const { providerWeb3, account, chainId } = useSDK(); + const { providerWeb3, account } = useSDK(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); + const waitForTx = useTxConfirmation(); const mergedOwner = owner ?? account; invariant(token != null, 'Token is required'); @@ -55,7 +56,6 @@ export const useApprove = ( const approve = useCallback( async ({ onTxStart, onTxSent, onTxAwaited } = {}) => { invariant(providerWeb3 != null, 'Web3 provider is required'); - invariant(chainId, 'chain id is required'); invariant(account, 'account is required'); await onTxStart?.(); const contractWeb3 = getERC20Contract(token, providerWeb3.getSigner()); @@ -83,7 +83,7 @@ export const useApprove = ( if (!isMultisig) { const receipt = await runWithTransactionLogger( 'Approve block confirmation', - () => staticRpcProvider.waitForTransaction(approveTxHash), + () => waitForTx(approveTxHash), ); await onTxAwaited?.(receipt); } @@ -94,13 +94,13 @@ export const useApprove = ( }, [ providerWeb3, - chainId, account, token, staticRpcProvider, allowanceQuery, spender, amount, + waitForTx, ], ); From 37043d9eda9e5870d7158537a975731f6bd331a6 Mon Sep 17 00:00:00 2001 From: Dmitrii Podlesnyi Date: Tue, 3 Sep 2024 18:51:27 +0700 Subject: [PATCH 27/40] feat: display dashed dex rate for inaccessible withdrawal wsteth amount --- .../withdrawals/request/form/options/options-picker.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/withdrawals/request/form/options/options-picker.tsx b/features/withdrawals/request/form/options/options-picker.tsx index 3f5230895..b84994e44 100644 --- a/features/withdrawals/request/form/options/options-picker.tsx +++ b/features/withdrawals/request/form/options/options-picker.tsx @@ -91,9 +91,13 @@ const DexButton: React.FC = ({ isActive, onClick }) => { fallbackValue: DEFAULT_VALUE_FOR_RATE, }); const isAnyDexEnabled = enabledDexes.length > 0; + const bestRateFloored = bestRate !== null && toFloor(bestRate); const bestRateValue = - !isPausedByTvlError && bestRate && isAnyDexEnabled - ? `1 : ${toFloor(bestRate)}` + !isPausedByTvlError && + isAnyDexEnabled && + bestRateFloored && + bestRateFloored !== '0' + ? `1 : ${bestRateFloored}` : '—'; return ( From 98c9c9eddca04453ad863e70b99f5c639352974f Mon Sep 17 00:00:00 2001 From: Anton Shalimov Date: Tue, 3 Sep 2024 18:04:14 +0300 Subject: [PATCH 28/40] fix: fix after develop merge --- features/withdrawals/hooks/useNftDataByTxHash.ts | 2 +- features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts | 2 +- features/wsteth/wrap/hooks/use-wrap-form-processing.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/features/withdrawals/hooks/useNftDataByTxHash.ts b/features/withdrawals/hooks/useNftDataByTxHash.ts index 15b7bc4fd..bbfd4ebaf 100644 --- a/features/withdrawals/hooks/useNftDataByTxHash.ts +++ b/features/withdrawals/hooks/useNftDataByTxHash.ts @@ -18,7 +18,7 @@ export const useNftDataByTxHash = (txHash: string | null) => { const { staticRpcProvider } = useCurrentStaticRpcProvider(); const swrNftApiData = useLidoSWR( - account && txHash ? ['swr:nft-data-by-tx-hash', txHash, account] : null, + address && txHash ? ['swr:nft-data-by-tx-hash', txHash, address] : null, async () => { if (!txHash || !address) return null; diff --git a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts index 1dccf9972..6baad8398 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts @@ -33,7 +33,7 @@ export const useUnwrapFormProcessor = ({ try { invariant(amount, 'amount should be presented'); invariant(address, 'address should be presented'); - const isMultisig = await isContract(account, staticRpcProvider); + const isMultisig = await isContract(address, staticRpcProvider); const willReceive = await wstETHContractRPC.getStETHByWstETH(amount); txModalStages.sign(amount, willReceive); diff --git a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts index 0e79906eb..e9e091728 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts @@ -2,7 +2,6 @@ import { useCallback } from 'react'; import invariant from 'tiny-invariant'; import { useAccount } from 'wagmi'; -import { useSDK } from '@lido-sdk/react'; import { useWSTETHContractRPC } from '@lido-sdk/react'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; @@ -39,7 +38,7 @@ export const useWrapFormProcessor = ({ try { invariant(amount, 'amount should be presented'); invariant(address, 'address should be presented'); - const isMultisig = await isContract(account, staticRpcProvider); + const isMultisig = await isContract(address, staticRpcProvider); const willReceive = await wstETHContractRPC.getWstETHByStETH(amount); if (isApprovalNeededBeforeWrap) { From 7be1cbaa4ff2679bec35f44fe61a927f0e18a5a7 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 5 Sep 2024 19:11:28 +0700 Subject: [PATCH 29/40] fix: ipfs rpc check on infra --- providers/ipfs-info-box-statuses.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/providers/ipfs-info-box-statuses.tsx b/providers/ipfs-info-box-statuses.tsx index 76823e0d0..c79cc6959 100644 --- a/providers/ipfs-info-box-statuses.tsx +++ b/providers/ipfs-info-box-statuses.tsx @@ -9,6 +9,7 @@ import { import { useLidoSWR, useLocalStorage, useSDK } from '@lido-sdk/react'; import invariant from 'tiny-invariant'; +import { config } from 'config'; import { useRpcUrl } from 'config/rpc'; import { SETTINGS_PATH } from 'consts/urls'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; @@ -56,7 +57,7 @@ export const IPFSInfoBoxStatusesProvider: FC = ({ const { data: isRPCAvailableRaw, initialLoading: isLoading } = useLidoSWR( `rpc-url-check-${rpcUrl}-${chainId}`, async () => await checkRpcUrl(rpcUrl, chainId), - STRATEGY_LAZY, + { ...STRATEGY_LAZY, isPaused: () => !config.ipfsMode }, ); const isRPCAvailable = isRPCAvailableRaw === true; From fef840ab8bdd12d335d59c5a205f9d60c0c077ec Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 5 Sep 2024 19:26:27 +0700 Subject: [PATCH 30/40] chore: up deps --- package.json | 20 ++++++------- yarn.lock | 82 ++++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 3bb507cc8..b75e90ed5 100644 --- a/package.json +++ b/package.json @@ -33,17 +33,17 @@ "@lido-sdk/helpers": "^1.6.0", "@lido-sdk/providers": "^1.4.15", "@lido-sdk/react": "^2.0.6", - "@lidofinance/analytics-matomo": "^0.45.0", - "@lidofinance/api-metrics": "^0.45.0", - "@lidofinance/api-rpc": "^0.45.0", - "@lidofinance/eth-api-providers": "^0.45.0", - "@lidofinance/eth-providers": "^0.45.0", + "@lidofinance/analytics-matomo": "^0.45.1", + "@lidofinance/api-metrics": "^0.45.1", + "@lidofinance/api-rpc": "^0.45.1", + "@lidofinance/eth-api-providers": "^0.45.1", + "@lidofinance/eth-providers": "^0.45.1", "@lidofinance/lido-ui": "^3.26.0", - "@lidofinance/next-api-wrapper": "^0.45.0", - "@lidofinance/next-ip-rate-limit": "^0.45.0", - "@lidofinance/next-pages": "^0.45.0", - "@lidofinance/rpc": "^0.45.0", - "@lidofinance/satanizer": "^0.45.0", + "@lidofinance/next-api-wrapper": "^0.45.1", + "@lidofinance/next-ip-rate-limit": "^0.45.1", + "@lidofinance/next-pages": "^0.45.1", + "@lidofinance/rpc": "^0.45.1", + "@lidofinance/satanizer": "^0.45.1", "@tanstack/react-query": "^5.51.21", "bignumber.js": "9.1.0", "copy-to-clipboard": "^3.3.1", diff --git a/yarn.lock b/yarn.lock index e1c483533..422beab7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2311,20 +2311,20 @@ tiny-invariant "^1.1.0" tiny-warning "^1.0.3" -"@lidofinance/analytics-matomo@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/analytics-matomo/-/analytics-matomo-0.45.0.tgz#288c82e193679c2c2654c4e434ccab81f99b7335" - integrity sha512-f+NAMPUrHlnCDzLwBPgoNNf8xmwW3AMm7J3Vb/Y0as96H7gMFoqvpfyzRwOF3ov9/ZIieLKoFThTn/VFWHVwYg== +"@lidofinance/analytics-matomo@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/analytics-matomo/-/analytics-matomo-0.45.1.tgz#f1ee0df1f6babb1f806e258831efd4ba568ad188" + integrity sha512-D35q+0XnqhWkKJidmT0iWKtLZ+KDj8z9J3t3wP+D0zi53+bP9dd7sU1ciZBUal1Sp0IjEzfiGVSiWKFQqyB8Pg== -"@lidofinance/api-metrics@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/api-metrics/-/api-metrics-0.45.0.tgz#5991b49544a9477c3f14e59dc6d84daed46c96d1" - integrity sha512-CMoV2RFzexw4lsNTW0RTMo6aaJ58iq7uWtmGuF0JxyB4HFfr+8uDr1v6IdJSK+qjfQeATfOC8EnSJ2C5QKUcpg== +"@lidofinance/api-metrics@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/api-metrics/-/api-metrics-0.45.1.tgz#0c093ebeb5fa3b2b73533ec933e0bbd20bbb37dd" + integrity sha512-iPzE4RzPxeApCF7+rIQcng/zos7gHY4ibTBkTF4Vt32P9aCtt70YinQ2Sf2KY8AnBeWhGJLXjvAPWr9P3OI18A== -"@lidofinance/api-rpc@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/api-rpc/-/api-rpc-0.45.0.tgz#0de2534c225c3a4271bb9cf55a228a17273aa222" - integrity sha512-t3UIuXt65nKQNP+e6thr6pAZ+8iKc7H/9vyD7PAhTuxNRMNj+PiVSZAErAjJjbo9/jbEHlgApsgpYwx4qAYIIA== +"@lidofinance/api-rpc@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/api-rpc/-/api-rpc-0.45.1.tgz#0a2002e233bd1233ed68d3a479a41ed93868a6d8" + integrity sha512-rTuLPs0EiOZa3tap2+zAgJ1/kpygjX+R5LgfkBw5oxZn1Dj9k+XvWuPAWBkHAsNVKonYTucD4F/NP0XhNlYuYQ== "@lidofinance/eslint-config@^0.34.0": version "0.34.0" @@ -2333,15 +2333,15 @@ dependencies: typescript "^4.7" -"@lidofinance/eth-api-providers@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/eth-api-providers/-/eth-api-providers-0.45.0.tgz#99160808cba97fca6823fdc9d15d4299db509808" - integrity sha512-B6F/Y0i8L6EiZt1AJk1b4FYMm/CqlBJQzFjWpwegP03wNOh/l84BxQcM/i9PPenV7mm3+RNC/qb4h7v6/L5lEA== +"@lidofinance/eth-api-providers@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/eth-api-providers/-/eth-api-providers-0.45.1.tgz#090e6a3957b664ce54641453357850cac0022a97" + integrity sha512-/8BbPUmBUhB8hRHZpzFXcK/i2jk7pmzhh2NJWjya+G3nSmXaw8U6PInpArB/ZxRAErs0zufUIOm4w7Qrr9mVPw== -"@lidofinance/eth-providers@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.45.0.tgz#8c49b4f3ae1316f3901fd8988ae8cd5918dbdfed" - integrity sha512-NAn+Eu0HL5cveC2gpl3fJLXRboKDJ0fO3ZTmgn94RuJEG4HoakXktaJUNnzFmLjUuHrhxUkqmIKtb9cT+C2bNg== +"@lidofinance/eth-providers@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.45.1.tgz#54cbd893c92c06f7ad605ebe0fd8059fc326f180" + integrity sha512-ugkRCI0BPFjWY2h/cDcM8IiibQ8i/wycUgVk2C+E7gCZkjL1YPxGZdnQkfFgIHXAtPhw2YeKHP+VC6HiZpVrZw== "@lidofinance/lido-ui@^3.18.0": version "3.21.0" @@ -2371,34 +2371,34 @@ ua-parser-js "^1.0.35" use-callback-ref "1.2.5" -"@lidofinance/next-api-wrapper@^0.45.0", "@lidofinance/next-api-wrapper@~0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-api-wrapper/-/next-api-wrapper-0.45.0.tgz#d97a225333393af9cf7bd71246ffd12e06b24b0c" - integrity sha512-vkpMRasRq3GWqK2QhlMWDKHPNSCFBbkqWZ9BSdeutWsfyHr1vgQ48u+bKQP5plkTviIFwFJ/Otw+qyCQcay3lA== +"@lidofinance/next-api-wrapper@^0.45.1", "@lidofinance/next-api-wrapper@~0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/next-api-wrapper/-/next-api-wrapper-0.45.1.tgz#59fe3c654c9276eaef8674302470fcc7bb9d8211" + integrity sha512-SUAc520aooNwNJZ0Q1391Yz8iONPiVMQrRC0wWXWDETMFy0ZhCsQ5VCuRKf6rmZwxYjK7K600GV8ke06IIetaw== -"@lidofinance/next-ip-rate-limit@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-ip-rate-limit/-/next-ip-rate-limit-0.45.0.tgz#09d53e4386d6d9f65541b141b9419a6139c15594" - integrity sha512-WoS91RrJhsgkWMM/YSGlG5hRnlOpy05j+bW/Z2zj+5V1968yzFg/nlYn7cJ8fdL1GC4PmG+KeKqnwIVdmTozWw== +"@lidofinance/next-ip-rate-limit@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/next-ip-rate-limit/-/next-ip-rate-limit-0.45.1.tgz#79ab181e9f125ad79c46a642dbeec5d8a194397c" + integrity sha512-8d0l6zi4IqX08Op5UCnGM5Z76tkX6GjzzfyjD6Mt7cNt/eNS8AGGKK9wbhUGmumu97l5L0hTZgEW3rNxA8+ptg== dependencies: - "@lidofinance/next-api-wrapper" "~0.45.0" + "@lidofinance/next-api-wrapper" "~0.45.1" -"@lidofinance/next-pages@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/next-pages/-/next-pages-0.45.0.tgz#45ec0441e74ced4c4f8d5b277c3f07b96513d653" - integrity sha512-64DvtGpSYSTscodFAem+gZ/1OvfkPrSNPV7Rbc3hY83vQj3lZ+DKhDBELv6pNOBh8KWvKSB2354vcNeuhKLuNg== +"@lidofinance/next-pages@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/next-pages/-/next-pages-0.45.1.tgz#b4c8fe555ba8f04b9cb2fe88a258632fe3e8552a" + integrity sha512-eaqZ2LUWsQg+lOhdzdL+WGQ3h5p7R+HYJ78q3UhnhWMrwZkMDNgxX6dWw7TudcD1yVC9yeZeT9tpfe7Hbd/XEQ== -"@lidofinance/rpc@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/rpc/-/rpc-0.45.0.tgz#40fadc5d6d9716c0a7c76ec0754b56a1e438f46d" - integrity sha512-oNXgOvLTAPPn+3TyIhuLMLNCJC85AgCUL4G1dHf2KBmlCKVyQzx5tITKKIGJ2GIdPWxFtjqtT+kBMQ51v0Ed7w== +"@lidofinance/rpc@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/rpc/-/rpc-0.45.1.tgz#53e2717ac434c4467ba0135d5144b64cb7af5548" + integrity sha512-F3ZgvBqM9sm8sQEiqGRO05fwBV/ZaexT4RmeSp3I6NcBtpaNuTf8z8IMDcslDhgk7HZZHt/VIAY+8TgVqV8jXw== dependencies: isomorphic-fetch "^3.0.0" -"@lidofinance/satanizer@^0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@lidofinance/satanizer/-/satanizer-0.45.0.tgz#9bb379accbfc3a0eb70f0af85fb45e8d1519fba9" - integrity sha512-m0kcCQADxl7sl7CoWJWFgwpBOI19HHY38oDxVfPYGhfnT+lJN1N20bZe9YKTwAXXowh+uDSO+NCFy86I+P84IQ== +"@lidofinance/satanizer@^0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@lidofinance/satanizer/-/satanizer-0.45.1.tgz#22fe644d7657b333074e9806e2b4229d210ba3a7" + integrity sha512-RC9jKEKykFhHFHUJNziFzvTP4LEIr1idRHgK96myKOn2w5GsiZkTd6SiaI7H9/X+Rg9ZbnTb0zcLZdYQPG946A== "@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": version "1.1.2" From 105b16752c35f180531785c83276b4c155dbd37e Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Thu, 5 Sep 2024 19:29:07 +0700 Subject: [PATCH 31/40] fix: sdk package --- package.json | 1 + yarn.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/package.json b/package.json index b75e90ed5..79c3ddb37 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@lidofinance/api-rpc": "^0.45.1", "@lidofinance/eth-api-providers": "^0.45.1", "@lidofinance/eth-providers": "^0.45.1", + "@lidofinance/lido-ethereum-sdk": "^3.4.0", "@lidofinance/lido-ui": "^3.26.0", "@lidofinance/next-api-wrapper": "^0.45.1", "@lidofinance/next-ip-rate-limit": "^0.45.1", diff --git a/yarn.lock b/yarn.lock index 422beab7d..cf3a92077 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1821,6 +1821,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@graphql-typed-document-node/core@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + "@humanwhocodes/config-array@^0.11.13": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -2343,6 +2348,16 @@ resolved "https://registry.yarnpkg.com/@lidofinance/eth-providers/-/eth-providers-0.45.1.tgz#54cbd893c92c06f7ad605ebe0fd8059fc326f180" integrity sha512-ugkRCI0BPFjWY2h/cDcM8IiibQ8i/wycUgVk2C+E7gCZkjL1YPxGZdnQkfFgIHXAtPhw2YeKHP+VC6HiZpVrZw== +"@lidofinance/lido-ethereum-sdk@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@lidofinance/lido-ethereum-sdk/-/lido-ethereum-sdk-3.4.0.tgz#8da4e41bf5d045e50017345bf12a363c17a5273d" + integrity sha512-dHCRhDah9QOlgZgf+GzMMEpE3wPEfudoczeuThW5kTFIUygmww1fPeV8y3HnOip5wpP/4dT4p3/ABCcy0bO0pw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + graphql "^16.8.1" + graphql-request "^6.1.0" + viem "^2.0.6" + "@lidofinance/lido-ui@^3.18.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lidofinance/lido-ui/-/lido-ui-3.21.0.tgz#da772e44ca7e96a062187858fe896ebc7e3d5cd1" @@ -2749,6 +2764,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== +"@noble/hashes@~1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -3127,6 +3147,11 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.7.tgz#fe973311a5c6267846aa131bc72e96c5d40d2b30" integrity sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g== +"@scure/base@~1.1.8": + version "1.1.8" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.8.tgz#8f23646c352f020c83bca750a82789e246d42b50" + integrity sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg== + "@scure/bip32@1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" @@ -3144,6 +3169,14 @@ "@noble/hashes" "~1.4.0" "@scure/base" "~1.1.6" +"@scure/bip39@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.4.0.tgz#664d4f851564e2e1d4bffa0339f9546ea55960a6" + integrity sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw== + dependencies: + "@noble/hashes" "~1.5.0" + "@scure/base" "~1.1.8" + "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" @@ -6808,6 +6841,19 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +graphql-request@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + +graphql@^16.8.1: + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + gray-matter@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" @@ -11401,6 +11447,21 @@ viem@2.18.8: webauthn-p256 "0.0.5" ws "8.17.1" +viem@^2.0.6: + version "2.21.2" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.21.2.tgz#8e6cae0babd12edee745e551b437d9e38985a97f" + integrity sha512-gTzwKbmyepEDUBKXs3GslTcg5KXfDIgQfHKNxIV9cs7Xout55F8NvHhNeBGBfuw1Ix4Vz8aCMFGYwX5a64CGFg== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.4.0" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.4.0" + abitype "1.0.5" + isows "1.0.4" + webauthn-p256 "0.0.5" + ws "8.17.1" + viem@^2.1.1: version "2.17.9" resolved "https://registry.yarnpkg.com/viem/-/viem-2.17.9.tgz#40ffd00a31621c8efdc4d49a58d5d30dc2d38d83" From 5b3e5dfdd6f04a610480fe780de880442151824e Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 6 Sep 2024 13:36:39 +0700 Subject: [PATCH 32/40] fix: increase balance cache-time --- shared/hooks/use-balance.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 56ebdade8..15184941e 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -196,7 +196,13 @@ const useTokenBalance = ( address: contract?.address, functionName: 'balanceOf', args: address && [address], - query: { enabled: !!address, select: nativeToBN }, + query: { + enabled: !!address, + select: nativeToBN, + // because we update on events we can have high staleTime + // this prevents loader when changing pages + staleTime: 30_000, + }, }); useEffect(() => { @@ -225,6 +231,7 @@ export const useStethBalance = ({ const { data: contract, isLoading } = useQuery({ queryKey: ['steth-contract', core.chainId], enabled: !!mergedAccount, + staleTime: Infinity, queryFn: async () => steth.getContract(), }); From 4665a1f72717fd3f928ba41bf358706efa4c0d9e Mon Sep 17 00:00:00 2001 From: Dmitrii Podlesnyi Date: Fri, 6 Sep 2024 14:44:32 +0700 Subject: [PATCH 33/40] feat: mode network multichain banner --- assets/icons/lido-multichain/mode.svg | 11 +++++++++++ consts/chains.ts | 1 + .../lido-multichain-fallback.tsx | 2 ++ shared/wallet/lido-multichain-fallback/styles.tsx | 8 ++++++++ 4 files changed, 22 insertions(+) create mode 100644 assets/icons/lido-multichain/mode.svg diff --git a/assets/icons/lido-multichain/mode.svg b/assets/icons/lido-multichain/mode.svg new file mode 100644 index 000000000..c3eaca426 --- /dev/null +++ b/assets/icons/lido-multichain/mode.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/consts/chains.ts b/consts/chains.ts index e5bb343be..506330d20 100644 --- a/consts/chains.ts +++ b/consts/chains.ts @@ -14,4 +14,5 @@ export enum LIDO_MULTICHAIN_CHAINS { Linea = 59144, Scroll = 534352, 'BNB Chain' = 56, + 'Mode Chain' = 34443, } diff --git a/shared/wallet/lido-multichain-fallback/lido-multichain-fallback.tsx b/shared/wallet/lido-multichain-fallback/lido-multichain-fallback.tsx index bbf6b4784..534e9479f 100644 --- a/shared/wallet/lido-multichain-fallback/lido-multichain-fallback.tsx +++ b/shared/wallet/lido-multichain-fallback/lido-multichain-fallback.tsx @@ -11,6 +11,7 @@ import { ReactComponent as PolygonLogo } from 'assets/icons/lido-multichain/poly import { ReactComponent as ZkSyncLogo } from 'assets/icons/lido-multichain/zk-sync.svg'; import { ReactComponent as ScrollLogo } from 'assets/icons/lido-multichain/scroll.svg'; import { ReactComponent as BNBLogo } from 'assets/icons/lido-multichain/bnb.svg'; +import { ReactComponent as ModeLogo } from 'assets/icons/lido-multichain/mode.svg'; import { config } from 'config'; import { useUserConfig } from 'config/user-config'; @@ -34,6 +35,7 @@ const multichainLogos = { [LIDO_MULTICHAIN_CHAINS['zkSync Era']]: ZkSyncLogo, [LIDO_MULTICHAIN_CHAINS.Scroll]: ScrollLogo, [LIDO_MULTICHAIN_CHAINS['BNB Chain']]: BNBLogo, + [LIDO_MULTICHAIN_CHAINS['Mode Chain']]: ModeLogo, }; const getChainLogo = (chainId: LIDO_MULTICHAIN_CHAINS) => { diff --git a/shared/wallet/lido-multichain-fallback/styles.tsx b/shared/wallet/lido-multichain-fallback/styles.tsx index 965a0057c..9ed5fe727 100644 --- a/shared/wallet/lido-multichain-fallback/styles.tsx +++ b/shared/wallet/lido-multichain-fallback/styles.tsx @@ -63,6 +63,14 @@ export const Wrap = styled((props) => )` #f0b90b 91.42% ); `; + case LIDO_MULTICHAIN_CHAINS['Mode Chain']: + return css` + background: linear-gradient( + 54.14deg, + #626931 -22.38%, + #b4c740 91.42% + ); + `; default: return css` background: linear-gradient( From 96cf9f74a732c14167ff4655659c786c4b14c957 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Fri, 6 Sep 2024 15:39:33 +0700 Subject: [PATCH 34/40] feat: use config for multichain banner --- IPFS.json | 8 +++-- config/external-config/types.ts | 1 + config/external-config/utils.ts | 55 ++++++++++++++++++++++++++------- config/groups/revalidation.ts | 13 -------- consts/external-links.ts | 2 +- shared/hooks/use-dapp-status.ts | 9 ++++-- 6 files changed, 59 insertions(+), 29 deletions(-) diff --git a/IPFS.json b/IPFS.json index df2d553d1..df6502566 100644 --- a/IPFS.json +++ b/IPFS.json @@ -3,7 +3,8 @@ "cid": "bafybeib3zmyqlmantvdd6i5q4ehmo4larvorgquyanne3uoqdbedwgh3aq", "leastSafeVersion": "0.36.1", "config": { - "enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"] + "enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"], + "multiChainBanner": [324, 10, 42161, 137, 8453, 5000, 59144, 534352, 56] } }, "5": { @@ -16,7 +17,10 @@ "cid": "bafybeibbsoqlofslw273b4ih2pdxfaz2zbjmred2ijog725tcmfoewix7y", "leastSafeVersion": "0.36.1", "config": { - "enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"] + "enabledWithdrawalDexes": ["one-inch", "paraswap", "bebop"], + "multiChainBanner": [ + 324, 10, 42161, 137, 8453, 5000, 59144, 534352, 56, 34443 + ] } } } diff --git a/config/external-config/types.ts b/config/external-config/types.ts index 92ea1ad04..7c0435064 100644 --- a/config/external-config/types.ts +++ b/config/external-config/types.ts @@ -12,6 +12,7 @@ export type ManifestEntry = { export type ManifestConfig = { enabledWithdrawalDexes: DexWithdrawalApi[]; + multiChainBanner: number[]; }; export type ExternalConfig = Omit & diff --git a/config/external-config/utils.ts b/config/external-config/utils.ts index 9dc7e8d86..ad7f72c1f 100644 --- a/config/external-config/utils.ts +++ b/config/external-config/utils.ts @@ -4,7 +4,45 @@ import { getDexConfig } from 'features/withdrawals/request/withdrawal-rates'; import FallbackLocalManifest from 'IPFS.json' assert { type: 'json' }; -// TODO: refactor on config expansion +const isEnabledDexesValid = (config: object) => { + if ( + !( + 'enabledWithdrawalDexes' in config && + Array.isArray(config.enabledWithdrawalDexes) + ) + ) + return false; + + const enabledWithdrawalDexes = config.enabledWithdrawalDexes; + + if ( + !enabledWithdrawalDexes.every( + (dex) => typeof dex === 'string' && dex !== '', + ) + ) + return false; + + return new Set(enabledWithdrawalDexes).size === enabledWithdrawalDexes.length; +}; + +const isMultiChainBannerValid = (config: object) => { + // allow empty config + if (!('multiChainBanner' in config) || !config.multiChainBanner) return true; + + if (!Array.isArray(config.multiChainBanner)) return false; + + const multiChainBanner = config.multiChainBanner; + + if ( + !multiChainBanner.every( + (chainId) => typeof chainId === 'number' && chainId > 0, + ) + ) + return false; + + return !(new Set(multiChainBanner).size !== multiChainBanner.length); +}; + export const isManifestEntryValid = ( entry?: unknown, ): entry is ManifestEntry => { @@ -18,16 +56,10 @@ export const isManifestEntryValid = ( entry.config ) { const config = entry.config; - if ( - 'enabledWithdrawalDexes' in config && - Array.isArray(config.enabledWithdrawalDexes) - ) { - const enabledWithdrawalDexes = config.enabledWithdrawalDexes; - return ( - new Set(enabledWithdrawalDexes).size === enabledWithdrawalDexes.length - ); - } - return false; + + return [isEnabledDexesValid, isMultiChainBannerValid] + .map((validator) => validator(config)) + .every((isValid) => isValid); } return false; }; @@ -39,6 +71,7 @@ export const getBackwardCompatibleConfig = ( enabledWithdrawalDexes: config.enabledWithdrawalDexes.filter( (dex) => !!getDexConfig(dex), ), + multiChainBanner: config.multiChainBanner ?? [], }; }; diff --git a/config/groups/revalidation.ts b/config/groups/revalidation.ts index 59c55f761..523d09e79 100644 --- a/config/groups/revalidation.ts +++ b/config/groups/revalidation.ts @@ -1,15 +1,2 @@ -import type { ManifestConfig, ManifestEntry } from 'config/external-config'; - export const DEFAULT_REVALIDATION = 60 * 15; // 15 minutes export const ERROR_REVALIDATION_SECONDS = 60; // 1 minute - -export const FALLBACK_CONFIG: ManifestConfig = { - enabledWithdrawalDexes: [], -}; - -export const FALLBACK_MANIFEST_ENTRY: ManifestEntry = { - cid: undefined, - ens: undefined, - leastSafeVersion: undefined, - config: FALLBACK_CONFIG, -}; diff --git a/consts/external-links.ts b/consts/external-links.ts index af4db36ed..a62599c78 100644 --- a/consts/external-links.ts +++ b/consts/external-links.ts @@ -5,6 +5,6 @@ export const LINK_ADD_NFT_GUIDE = `${config.helpOrigin}/en/articles/7858367-how- export const OPEN_OCEAN_REFERRAL_ADDRESS = '0xbb1263222b2c020f155d409dba05c4a3861f18f8'; -// for dev and local testing you can set to 'http:/localhost:3000/runtime/IPFS.json' and have file at /public/runtime/IPFS.json +// for dev and local testing you can set to 'http://localhost:3000/runtime/IPFS.json' and have file at /public/runtime/IPFS.json export const IPFS_MANIFEST_URL = 'https://raw.githubusercontent.com/lidofinance/ethereum-staking-widget/main/IPFS.json'; diff --git a/shared/hooks/use-dapp-status.ts b/shared/hooks/use-dapp-status.ts index 2ddc41cb1..b4f6909b3 100644 --- a/shared/hooks/use-dapp-status.ts +++ b/shared/hooks/use-dapp-status.ts @@ -4,14 +4,19 @@ import { useAccount } from 'wagmi'; import { LIDO_MULTICHAIN_CHAINS } from 'consts/chains'; import { useIsSupportedChain } from './use-is-supported-chain'; +import { useConfig } from 'config'; export const useDappStatus = () => { + const { multiChainBanner } = useConfig().externalConfig; const { chainId, isConnected: isWalletConnected } = useAccount(); const isSupportedChain = useIsSupportedChain(); const isLidoMultichainChain = useMemo( - () => !!chainId && !!LIDO_MULTICHAIN_CHAINS[chainId], - [chainId], + () => + !!chainId && + !!LIDO_MULTICHAIN_CHAINS[chainId] && + multiChainBanner.includes(chainId), + [chainId, multiChainBanner], ); const isDappActive = useMemo(() => { From b4142caf84a42898de3944612c7e1d05804dc0fe Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 18:08:37 +0700 Subject: [PATCH 35/40] fix: higher block fetch interval --- config/groups/web3.ts | 2 +- shared/hooks/use-balance.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/config/groups/web3.ts b/config/groups/web3.ts index 0cbb31961..3e072920d 100644 --- a/config/groups/web3.ts +++ b/config/groups/web3.ts @@ -1,7 +1,7 @@ import { parseEther } from '@ethersproject/units'; // interval in ms for RPC event polling for token balance and tx updates -export const PROVIDER_POLLING_INTERVAL = 7_000; +export const PROVIDER_POLLING_INTERVAL = 12_000; // how long in ms to wait for RPC batching(multicall and provider) export const PROVIDER_BATCH_TIME = 150; diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 15184941e..000ecfd1e 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -16,6 +16,7 @@ import { import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; import type { GetBalanceData } from 'wagmi/query'; import type { Address, WatchContractEventOnLogsFn } from 'viem'; +import { config } from 'config'; const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); @@ -24,10 +25,22 @@ const balanceToBN = (data: GetBalanceData) => nativeToBN(data.value); export const useEthereumBalance = () => { const queryClient = useQueryClient(); const { address } = useAccount(); - const { data: blockNumber } = useBlockNumber({ watch: !!address }); + const { data: blockNumber } = useBlockNumber({ + watch: { + poll: true, + pollingInterval: config.PROVIDER_POLLING_INTERVAL, + enabled: !!address, + }, + cacheTime: config.PROVIDER_POLLING_INTERVAL, + }); const queryData = useBalance({ address, - query: { select: balanceToBN, staleTime: 7000, enabled: !!address }, + query: { + select: balanceToBN, + // because we subscribe to block + staleTime: Infinity, + enabled: !!address, + }, }); useEffect(() => { From de5b5a8d974b981df6546d3d86cf9c66c14a7a0e Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 18:28:03 +0700 Subject: [PATCH 36/40] fix: disable agressive default --- providers/web3.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/providers/web3.tsx b/providers/web3.tsx index 5eeeccdd1..a7abd995f 100644 --- a/providers/web3.tsx +++ b/providers/web3.tsx @@ -22,7 +22,13 @@ type ChainsList = [wagmiChains.Chain, ...wagmiChains.Chain[]]; const wagmiChainsArray = Object.values(wagmiChains) as any as ChainsList; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, +}); const Web3Provider: FC = ({ children }) => { const { From 7a65a1e0e4116b6f9a914fec68e8c845d1151042 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 18:34:48 +0700 Subject: [PATCH 37/40] fix: unstable query key --- shared/hooks/use-balance.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/hooks/use-balance.ts b/shared/hooks/use-balance.ts index 000ecfd1e..3eabdd4b3 100644 --- a/shared/hooks/use-balance.ts +++ b/shared/hooks/use-balance.ts @@ -33,6 +33,7 @@ export const useEthereumBalance = () => { }, cacheTime: config.PROVIDER_POLLING_INTERVAL, }); + const queryData = useBalance({ address, query: { @@ -50,7 +51,9 @@ export const useEthereumBalance = () => { // dedups rpc requests { cancelRefetch: false }, ); - }, [blockNumber, queryClient, queryData.queryKey]); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [blockNumber]); return queryData; }; From dcf6b230ff8a21abdc11f391cbc571b494ffaa58 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 18:48:24 +0700 Subject: [PATCH 38/40] fix: refetch allowance data --- features/withdrawals/hooks/contract/useRequest.ts | 8 +++++++- features/wsteth/wrap/hooks/use-wrap-tx-approve.ts | 3 +++ .../wrap/wrap-form-context/wrap-form-context.tsx | 10 +++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index 990cbe3ac..82c7d17c3 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -218,6 +218,7 @@ export const useWithdrawalRequest = ({ needsApprove, allowance, isLoading: loadingUseApprove, + refetch: refetchAllowance, } = useApprove( valueBN, tokenContract.address, @@ -321,7 +322,11 @@ export const useWithdrawalRequest = ({ ); } - await onConfirm?.(); + await Promise.all([ + onConfirm?.(), + isApprovalFlow && + refetchAllowance({ throwOnError: false, cancelRefetch: false }), + ]); txModalStages.success(amount, token, txHash); return true; } catch (error) { @@ -341,6 +346,7 @@ export const useWithdrawalRequest = ({ needsApprove, onConfirm, onRetry, + refetchAllowance, txModalStages, waitForTx, ], diff --git a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts index 549736d85..f8fd6396f 100644 --- a/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts +++ b/features/wsteth/wrap/hooks/use-wrap-tx-approve.ts @@ -32,6 +32,7 @@ export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { needsApprove, allowance, isLoading: isApprovalLoading, + refetch: refetchAllowance, } = useApprove( amount, stethTokenAddress, @@ -49,6 +50,7 @@ export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { allowance, isApprovalLoading, isApprovalNeededBeforeWrap, + refetchAllowance, }), [ allowance, @@ -56,6 +58,7 @@ export const useWrapTxApprove = ({ amount, token }: UseWrapTxApproveArgs) => { needsApprove, isApprovalLoading, processApproveTx, + refetchAllowance, ], ); }; diff --git a/features/wsteth/wrap/wrap-form-context/wrap-form-context.tsx b/features/wsteth/wrap/wrap-form-context/wrap-form-context.tsx index 245582359..b2bf010d4 100644 --- a/features/wsteth/wrap/wrap-form-context/wrap-form-context.tsx +++ b/features/wsteth/wrap/wrap-form-context/wrap-form-context.tsx @@ -5,6 +5,7 @@ import { useMemo, createContext, useContext, + useCallback, } from 'react'; import { useForm, FormProvider } from 'react-hook-form'; import { useWrapTxApprove } from '../hooks/use-wrap-tx-approve'; @@ -72,9 +73,16 @@ export const WrapFormProvider: FC = ({ children }) => { const approvalData = useWrapTxApprove({ amount: amount ?? Zero, token }); const isSteth = token === TOKENS_TO_WRAP.STETH; + const onConfirm = useCallback(async () => { + await Promise.allSettled([ + networkData.revalidateWrapFormData(), + approvalData.refetchAllowance(), + ]); + }, [networkData, approvalData]); + const processWrapFormFlow = useWrapFormProcessor({ approvalData, - onConfirm: networkData.revalidateWrapFormData, + onConfirm, onRetry: retryFire, }); From 5e226a81d177cc65673d090c5ed2a9c6e1ea9569 Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 20:22:22 +0700 Subject: [PATCH 39/40] fix: allowance events --- shared/hooks/use-allowance.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/shared/hooks/use-allowance.ts b/shared/hooks/use-allowance.ts index 2111f11ac..63061d661 100644 --- a/shared/hooks/use-allowance.ts +++ b/shared/hooks/use-allowance.ts @@ -1,7 +1,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { BigNumber } from 'ethers'; import { useCallback, useMemo } from 'react'; -import { Address } from 'viem'; +import { Address, WatchContractEventOnLogsFn } from 'viem'; import { useReadContract, useWatchContractEvent } from 'wagmi'; const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); @@ -50,6 +50,12 @@ const Erc20AllowanceAbi = [ }, ] as const; +type OnLogsFn = WatchContractEventOnLogsFn< + typeof Erc20AllowanceAbi, + 'Transfer' | 'Approval', + true +>; + type UseAllowanceProps = { token: Address; account: Address; @@ -72,14 +78,19 @@ export const useAllowance = ({ query: { enabled, select: nativeToBN }, }); - const onLogs = useCallback(() => { - void queryClient.invalidateQueries( - { - queryKey: allowanceQuery.queryKey, - }, - { cancelRefetch: false }, - ); - }, [allowanceQuery.queryKey, queryClient]); + const onLogs: OnLogsFn = useCallback( + () => { + void queryClient.invalidateQueries( + { + queryKey: allowanceQuery.queryKey, + }, + { cancelRefetch: false }, + ); + }, + // queryKey is unstable + // eslint-disable-next-line react-hooks/exhaustive-deps + [account, spender, token], + ); useWatchContractEvent({ abi: Erc20AllowanceAbi, From 491b420a4f501a2a14e6007483f556449dbb206c Mon Sep 17 00:00:00 2001 From: Evgeny Taktarov Date: Mon, 9 Sep 2024 21:05:56 +0700 Subject: [PATCH 40/40] test: remove deprecated endpoints from test --- test/consts.ts | 69 -------------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/test/consts.ts b/test/consts.ts index 8dae119cd..47faf7e0a 100644 --- a/test/consts.ts +++ b/test/consts.ts @@ -13,68 +13,7 @@ export interface PostRequest { schema: object; } -const FLOAT_REGEX = /^\d+(\.\d+)?$/; - export const GET_REQUESTS: GetRequest[] = [ - { - uri: '/api/oneinch-rate?token=ETH', - isDeprecated: true, - schema: { - type: 'object', - properties: { - rate: { type: 'number', min: 0 }, - toReceive: { type: 'string' }, - fromAmount: { type: 'string' }, - }, - required: ['rate', 'toReceive', 'fromAmount'], - additionalProperties: false, - }, - }, - { - uri: `/api/short-lido-stats?chainId=${CONFIG.STAND_CONFIG.chainId}`, - isDeprecated: true, - schema: { - type: 'object', - properties: { - uniqueAnytimeHolders: { type: 'string' }, - uniqueHolders: { type: 'string' }, - totalStaked: { type: 'string' }, - marketCap: { type: 'number' }, - }, - required: [ - 'totalStaked', - 'marketCap', - 'uniqueAnytimeHolders', - 'uniqueHolders', - ], - additionalProperties: true, - }, - }, - { - uri: '/api/eth-apr', - isDeprecated: true, - schema: { type: 'string', pattern: FLOAT_REGEX }, - }, - { - uri: '/api/totalsupply', - isDeprecated: true, - schema: { type: 'string', pattern: FLOAT_REGEX }, - }, - { - uri: '/api/eth-price', - isDeprecated: true, - schema: { - type: 'object', - properties: { - price: { - type: 'number', - min: 0, - }, - }, - required: ['price'], - additionalProperties: true, - }, - }, { uri: '/api/rewards?address=0x87c0e047F4e4D3e289A56a36570D4CB957A37Ef1¤cy=usd&onlyRewards=false&archiveRate=true&skip=0&limit=10', skipTestnet: true, // api/rewards don't work on testnet @@ -121,14 +60,6 @@ export const GET_REQUESTS: GetRequest[] = [ }, }, }, - { - uri: '/api/sma-steth-apr', - isDeprecated: true, - schema: { - type: 'string', - pattern: FLOAT_REGEX, - }, - }, ]; export const POST_REQUESTS: PostRequest[] = [