diff --git a/consts/chains.ts b/consts/chains.ts index c0435495c..8c6d1f86b 100644 --- a/consts/chains.ts +++ b/consts/chains.ts @@ -22,13 +22,6 @@ export enum LIDO_MULTICHAIN_CHAINS { 'Zircuit Chain' = 48900, } -// TODO: move to legacy lido-js-sdk package -export const SDK_LEGACY_SUPPORTED_CHAINS = [ - CHAINS.Mainnet, - CHAINS.Holesky, - CHAINS.Sepolia, -]; - // TODO: move to @lidofinance/lido-ethereum-sdk package export const isSDKSupportedL2Chain = (chainId?: CHAINS) => { return chainId && !!LIDO_L2_CONTRACT_ADDRESSES[chainId]; diff --git a/features/ipfs/security-status-banner/security-status-banner.tsx b/features/ipfs/security-status-banner/security-status-banner.tsx index 8a33b6949..2c8acb86b 100644 --- a/features/ipfs/security-status-banner/security-status-banner.tsx +++ b/features/ipfs/security-status-banner/security-status-banner.tsx @@ -11,7 +11,7 @@ import { WarningBlock, WarningTitle, } from './styles'; -import { useVersionCheck } from './use-version-check'; +import { useVersionStatus } from './use-version-status'; const LIDO_TWITTER_LINK = 'https://twitter.com/LidoFinance'; @@ -100,7 +100,7 @@ export const SecurityStatusBanner = () => { isVersionUnsafe, isNotVerifiable, data, - } = useVersionCheck(); + } = useVersionStatus(); const { content, canClose, showTwitterLink } = warningContent({ isUpdateAvailable, diff --git a/features/ipfs/security-status-banner/use-version-check.ts b/features/ipfs/security-status-banner/use-version-status.ts similarity index 92% rename from features/ipfs/security-status-banner/use-version-check.ts rename to features/ipfs/security-status-banner/use-version-status.ts index f74449dbf..d18684484 100644 --- a/features/ipfs/security-status-banner/use-version-check.ts +++ b/features/ipfs/security-status-banner/use-version-status.ts @@ -6,7 +6,7 @@ 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 { useDappStatus } from 'modules/web3'; import { overrideWithQAMockBoolean } from 'utils/qa'; import { isVersionLess } from './utils'; @@ -18,8 +18,8 @@ export const NO_SAFE_VERSION = 'NONE_AVAILABLE'; 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 { isDappActive } = useDappStatus(); +export const useVersionStatus = () => { + const { isWalletConnected } = useDappStatus(); const { setIsWalletConnectionAllowed } = useUserConfig(); const { forceDisconnect } = useForceDisconnect(); const [areConditionsAccepted, setConditionsAccepted] = useState(false); @@ -80,14 +80,17 @@ export const useVersionCheck = () => { if (isVersionUnsafe) { setIsWalletConnectionAllowed(false); } - if (isVersionUnsafe || (config.ipfsMode && isNotVerifiable)) { + if ( + isVersionUnsafe || + (config.ipfsMode && isNotVerifiable && isWalletConnected) + ) { forceDisconnect(); } }, [ - isDappActive, forceDisconnect, isNotVerifiable, isVersionUnsafe, + isWalletConnected, setIsWalletConnectionAllowed, ]); diff --git a/features/rewards/components/rewardsListContent/RewardsListContent.tsx b/features/rewards/components/rewardsListContent/RewardsListContent.tsx index db6be73b6..f4eca9a9a 100644 --- a/features/rewards/components/rewardsListContent/RewardsListContent.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListContent.tsx @@ -5,8 +5,7 @@ import { Zero } from '@ethersproject/constants'; import { useRewardsHistory } from 'features/rewards/hooks'; import { ErrorBlockNoSteth } from 'features/rewards/components/errorBlocks/ErrorBlockNoSteth'; import { RewardsTable } from 'features/rewards/components/rewardsTable'; -import { useStethBalance } from 'shared/hooks/use-balance'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useStethBalance, useDappStatus } from 'modules/web3'; import { RewardsListsEmpty } from './RewardsListsEmpty'; import { RewardsListErrorMessage } from './RewardsListErrorMessage'; @@ -20,8 +19,7 @@ import { import type { Address } from 'viem'; export const RewardsListContent: FC = () => { - const { isWalletConnected, isSupportedChain, isAccountActiveOnL2 } = - useDappStatus(); + const { isSupportedChain } = useDappStatus(); const { address, error, @@ -39,8 +37,7 @@ export const RewardsListContent: FC = () => { }); const hasSteth = stethBalance?.gt(Zero); - if ((isWalletConnected && !isSupportedChain) || isAccountActiveOnL2) - return ; + if (!isSupportedChain) return ; if (!data && !initialLoading && !error) return ; diff --git a/features/rewards/components/rewardsListContent/RewardsListsEmpty.tsx b/features/rewards/components/rewardsListContent/RewardsListsEmpty.tsx index 78baeb4ce..b483d88cf 100644 --- a/features/rewards/components/rewardsListContent/RewardsListsEmpty.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListsEmpty.tsx @@ -5,7 +5,7 @@ import { Button, Divider } from '@lidofinance/lido-ui'; import { useUserConfig } from 'config/user-config'; import { MATOMO_CLICK_EVENTS } from 'consts/matomo-click-events'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { RewardsListEmptyButtonWrapper, RewardsListEmptyWrapper, diff --git a/features/rewards/components/rewardsListContent/RewardsListsUnsupportedChain.tsx b/features/rewards/components/rewardsListContent/RewardsListsUnsupportedChain.tsx index f184685eb..1ace34774 100644 --- a/features/rewards/components/rewardsListContent/RewardsListsUnsupportedChain.tsx +++ b/features/rewards/components/rewardsListContent/RewardsListsUnsupportedChain.tsx @@ -1,22 +1,20 @@ import { FC } from 'react'; import { Divider } from '@lidofinance/lido-ui'; -import { useConfig } from 'config'; -import { CHAINS } from 'consts/chains'; import { RewardsListEmptyWrapper } from './RewardsListsEmptyStyles'; +import { useDappStatus } from 'modules/web3'; +import { joinWithOr } from 'utils/join-with-or'; export const RewardsListsUnsupportedChain: FC = () => { - const { - config: { defaultChain }, - } = useConfig(); + const { supportedChainLabels } = useDappStatus(); return ( <>

- Please switch to {CHAINS[defaultChain]} in your wallet to see the - stats. + Please switch to {joinWithOr(supportedChainLabels)} in your wallet to + see the stats.

diff --git a/features/rewards/components/rewardsListHeader/RewardsListHeader.tsx b/features/rewards/components/rewardsListHeader/RewardsListHeader.tsx index 9eb952940..d86f7d205 100644 --- a/features/rewards/components/rewardsListHeader/RewardsListHeader.tsx +++ b/features/rewards/components/rewardsListHeader/RewardsListHeader.tsx @@ -1,6 +1,6 @@ import { FC } from 'react'; import { useRewardsHistory } from 'features/rewards/hooks'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { LeftOptions } from './LeftOptions'; import { RightOptions } from './RightOptions'; @@ -8,24 +8,18 @@ import { RewardsListHeaderStyle } from './styles'; import { TitleStyle } from './styles'; export const RewardsListHeader: FC = () => { - const { isWalletConnected, isSupportedChain, isAccountActiveOnL2 } = - useDappStatus(); + const { isSupportedChain } = useDappStatus(); const { error, data } = useRewardsHistory(); return ( Reward history - {!error && - data && - data?.events.length > 0 && - (!isWalletConnected || - (isWalletConnected && isSupportedChain) || - !isAccountActiveOnL2) && ( - <> - - - - )} + {!error && data && data?.events.length > 0 && isSupportedChain && ( + <> + + + + )} ); }; diff --git a/features/rewards/features/top-card/top-card.tsx b/features/rewards/features/top-card/top-card.tsx index c1b3092b7..a499f549a 100644 --- a/features/rewards/features/top-card/top-card.tsx +++ b/features/rewards/features/top-card/top-card.tsx @@ -1,20 +1,15 @@ import { FC, useEffect, useState } from 'react'; -import { CHAINS } from '@lido-sdk/constants'; -import { getConfig } from 'config'; import { StatsWrapper } from 'features/rewards/components/statsWrapper'; import { Stats } from 'features/rewards/components/stats'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { Fallback } from 'shared/wallet'; import { Wallet } from './wallet'; +import { useDappStatus } from 'modules/web3'; export const TopCard: FC = () => { - const { defaultChain } = getConfig(); const [visible, setVisible] = useState(false); - const { isWalletConnected, isSupportedChain, isAccountActiveOnL2 } = - useDappStatus(); - + const { isSupportedChain } = useDappStatus(); // fix flash after reload page useEffect(() => { setVisible(true); @@ -22,12 +17,11 @@ export const TopCard: FC = () => { if (!visible) return null; + // We allow unconnected wallet and don't show multichain for rewards return ( <> - {(isWalletConnected && !isSupportedChain) || isAccountActiveOnL2 ? ( - + {!isSupportedChain ? ( + ) : ( )} diff --git a/features/rewards/hooks/useGetCurrentAddress.ts b/features/rewards/hooks/useGetCurrentAddress.ts index 00fe2257b..e618b952e 100644 --- a/features/rewards/hooks/useGetCurrentAddress.ts +++ b/features/rewards/hooks/useGetCurrentAddress.ts @@ -6,7 +6,7 @@ import { useSDK } from '@lido-sdk/react'; import { resolveEns, isValidEns, isValidAddress } from 'features/rewards/utils'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; type UseGetCurrentAddress = () => { address: string; diff --git a/features/settings/settings-form/settings-form.tsx b/features/settings/settings-form/settings-form.tsx index a9cc22e49..06920792e 100644 --- a/features/settings/settings-form/settings-form.tsx +++ b/features/settings/settings-form/settings-form.tsx @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { useForm } from 'react-hook-form'; -import { useSDK } from '@lido-sdk/react'; import { Button, ToastSuccess, Block, Input } from '@lidofinance/lido-ui'; import { useUserConfig } from 'config/user-config'; @@ -15,6 +14,7 @@ import { DescriptionTitle, SettingsFormWrap, } from './styles'; +import { useDappStatus } from 'modules/web3'; type FormValues = { rpcUrl: string; @@ -22,7 +22,7 @@ type FormValues = { export const SettingsForm = () => { const { savedUserConfig, setSavedUserConfig } = useUserConfig(); - const { chainId } = useSDK(); + const { chainId } = useDappStatus(); const formMethods = useForm({ mode: 'onChange', diff --git a/features/stake/stake-form/controls/stake-amount-input.tsx b/features/stake/stake-form/controls/stake-amount-input.tsx index 58a307a9c..d0c82cfbc 100644 --- a/features/stake/stake-form/controls/stake-amount-input.tsx +++ b/features/stake/stake-form/controls/stake-amount-input.tsx @@ -1,12 +1,10 @@ import { Eth } from '@lidofinance/lido-ui'; import { TokenAmountInputHookForm } from 'shared/hook-form/controls/token-amount-input-hook-form'; import { useStakeFormData } from '../stake-form-context'; -import { useStakingLimitWarning } from 'shared/hooks/use-staking-limit-warning'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useStakingLimitWarning, useDappStatus } from 'modules/web3'; export const StakeAmountInput = () => { - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); + const { isWalletConnected, isDappActive } = useDappStatus(); const { maxAmount, stakingLimitInfo } = useStakeFormData(); const { limitWarning, limitError } = useStakingLimitWarning( stakingLimitInfo?.stakeLimitLevel, @@ -14,7 +12,7 @@ export const StakeAmountInput = () => { return ( { - const { isDappActive, isAccountActiveOnL2 } = useDappStatus(); + const { isDappActive } = useDappStatus(); const { stakingLimitInfo } = useStakeFormData(); return ( { const stethContractWeb3 = useSTETHContractWeb3(); - const { address } = useAccount(); + const { address } = useDappStatus(); const stethContract = useSTETHContractRPC(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { providerWeb3 } = useSDK(); diff --git a/features/stake/stake-form/wallet/wallet.tsx b/features/stake/stake-form/wallet/wallet.tsx index d5f84d706..daa1b3f7e 100644 --- a/features/stake/stake-form/wallet/wallet.tsx +++ b/features/stake/stake-form/wallet/wallet.tsx @@ -1,41 +1,27 @@ -import { memo } from 'react'; -import { useAccount } from 'wagmi'; - -import { CHAINS, TOKENS } from '@lido-sdk/constants'; +import { TOKENS } from '@lido-sdk/constants'; import { Divider, Question, Tooltip } from '@lidofinance/lido-ui'; -import { getConfig } from 'config'; import { LIDO_APR_TOOLTIP_TEXT, DATA_UNAVAILABLE } from 'consts/text'; import { TokenToWallet } from 'shared/components'; import { FormatToken } from 'shared/formatters'; import { useLidoApr } from 'shared/hooks'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; import { useTokenAddress } from 'shared/hooks/use-token-address'; -import { useLidoMultichainFallbackCondition } from 'shared/hooks/use-lido-multichain-fallback-condition'; -import { - CardAccount, - CardBalance, - CardRow, - Fallback, - LidoMultichainFallback, -} from 'shared/wallet'; -import type { WalletComponentType } from 'shared/wallet/types'; +import { CardAccount, CardBalance, CardRow, Fallback } from 'shared/wallet'; import { useStakeFormData } from '../stake-form-context'; import { LimitMeter } from './limit-meter'; import { FlexCenter, LidoAprStyled, StyledCard } from './styles'; -const WalletComponent: WalletComponentType = (props) => { - const { address } = useAccount(); +const WalletComponent = () => { const { stakeableEther, stethBalance, loading } = useStakeFormData(); const stethAddress = useTokenAddress(TOKENS.STETH); const lidoApr = useLidoApr(); return ( - + { /> } /> - + @@ -99,34 +85,10 @@ const WalletComponent: WalletComponentType = (props) => { ); }; -export const Wallet: WalletComponentType = memo((props) => { - const { defaultChain } = getConfig(); - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); - const { showLidoMultichainFallback } = useLidoMultichainFallbackCondition(); - - if (showLidoMultichainFallback) { - return ; - } - - if (isAccountActiveOnL2) { - return ( - - ); - } - - if (isWalletConnected && !isDappActive) { - return ( - - ); - } - - if (!isDappActive) { - return ; - } - - return ; -}); +export const Wallet = () => { + return ( + + + + ); +}; diff --git a/features/stake/stake-page.tsx b/features/stake/stake-page.tsx index 2b3571399..e19e81b62 100644 --- a/features/stake/stake-page.tsx +++ b/features/stake/stake-page.tsx @@ -1,9 +1,9 @@ -import { Stake } from './stake'; import Head from 'next/head'; -import { FC } from 'react'; + import { Layout } from 'shared/components'; +import { Stake } from './stake'; -export { Stake } from './stake'; +import type { FC } from 'react'; export const StakePage: FC = () => { return ( diff --git a/features/stake/stake.tsx b/features/stake/stake.tsx index 237eb888c..a2444e0c7 100644 --- a/features/stake/stake.tsx +++ b/features/stake/stake.tsx @@ -1,5 +1,5 @@ import { FaqPlaceholder } from 'features/ipfs'; -import { useWagmiKey } from 'shared/hooks/use-wagmi-key'; +import { useWagmiKey } from 'modules/web3'; import NoSSRWrapper from 'shared/components/no-ssr-wrapper'; import { OnlyInfraRender } from 'shared/components/only-infra-render'; 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 555257a7e..861f00837 100644 --- a/features/withdrawals/claim/claim-form-context/claim-form-context.tsx +++ b/features/withdrawals/claim/claim-form-context/claim-form-context.tsx @@ -16,7 +16,7 @@ import { FormControllerContext, FormControllerContextValueType, } from 'shared/hook-form/form-controller'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { ClaimFormInputType, ClaimFormValidationContext } from './types'; import { claimFormValidationResolver } from './validation'; diff --git a/features/withdrawals/claim/form/requests-list/request-item.tsx b/features/withdrawals/claim/form/requests-list/request-item.tsx index a811f8cdd..8bae6e15c 100644 --- a/features/withdrawals/claim/form/requests-list/request-item.tsx +++ b/features/withdrawals/claim/form/requests-list/request-item.tsx @@ -1,6 +1,5 @@ import { forwardRef } from 'react'; import { useFormState, useWatch } from 'react-hook-form'; -import { useAccount } from 'wagmi'; import { Checkbox, @@ -15,6 +14,7 @@ import { useClaimFormData, ClaimFormInputType } from '../../claim-form-context'; import { getNFTUrl } from 'utils'; import { RequestStyled, LinkStyled } from './styles'; +import { useSDK } from '@lido-sdk/react'; type RequestItemProps = { token_id: string; @@ -24,7 +24,7 @@ type RequestItemProps = { export const RequestItem = forwardRef( ({ token_id, name, disabled, index, ...props }, ref) => { - const { chainId } = useAccount(); + const { chainId } = useSDK(); const { isSubmitting } = useFormState(); const { canSelectMore, maxSelectedCountReason } = useClaimFormData(); const { checked, status } = useWatch< diff --git a/features/withdrawals/claim/form/requests-list/requests-empty.tsx b/features/withdrawals/claim/form/requests-list/requests-empty.tsx index e959f5cc0..120c56d3c 100644 --- a/features/withdrawals/claim/form/requests-list/requests-empty.tsx +++ b/features/withdrawals/claim/form/requests-list/requests-empty.tsx @@ -1,18 +1,10 @@ -import { CHAINS } from '@lido-sdk/constants'; -import { useUserConfig } from 'config/user-config'; - import { EmptyText, WrapperEmpty } from './styles'; +import { useDappStatus } from 'modules/web3'; +import { joinWithOr } from 'utils/join-with-or'; -type RequestsEmptyProps = { - isWalletConnected?: boolean; - isDappActive?: boolean; -}; - -export const RequestsEmpty = ({ - isWalletConnected, - isDappActive, -}: RequestsEmptyProps) => { - const { defaultChain } = useUserConfig(); +export const RequestsEmpty = () => { + const { isWalletConnected, isSupportedChain, supportedChainLabels } = + useDappStatus(); if (!isWalletConnected) { return ( @@ -22,11 +14,12 @@ export const RequestsEmpty = ({ ); } - if (isWalletConnected && !isDappActive) { + if (isWalletConnected && !isSupportedChain) { return ( - Switch to {CHAINS[defaultChain]} to see your withdrawal requests + Switch to {joinWithOr(supportedChainLabels)} to see your withdrawal + requests ); diff --git a/features/withdrawals/claim/form/requests-list/requests-list.tsx b/features/withdrawals/claim/form/requests-list/requests-list.tsx index 8bb23403c..3b14722bc 100644 --- a/features/withdrawals/claim/form/requests-list/requests-list.tsx +++ b/features/withdrawals/claim/form/requests-list/requests-list.tsx @@ -1,5 +1,5 @@ import { useFieldArray, useFormContext, useFormState } from 'react-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { ClaimFormInputType } from '../../claim-form-context'; import { RequestItem } from './request-item'; @@ -8,24 +8,19 @@ import { Wrapper } from './styles'; import { RequestsLoader } from './requests-loader'; export const RequestsList: React.FC = () => { - const { isWalletConnected, isDappActiveOnL1 } = useDappStatus(); + const { isDappActive } = useDappStatus(); const { isLoading } = useFormState(); const { register } = useFormContext(); const { fields } = useFieldArray({ name: 'requests', }); - if (isLoading) { - return ; + if (!isDappActive || (fields.length === 0 && !isLoading)) { + return ; } - if (!isDappActiveOnL1 || fields.length === 0) { - return ( - - ); + if (isLoading) { + return ; } return ( diff --git a/features/withdrawals/claim/form/submit-button.tsx b/features/withdrawals/claim/form/submit-button.tsx index 2d74e0a6a..3f5f18f79 100644 --- a/features/withdrawals/claim/form/submit-button.tsx +++ b/features/withdrawals/claim/form/submit-button.tsx @@ -1,29 +1,25 @@ import { useFormState } from 'react-hook-form'; -import { useAccount } from 'wagmi'; import { Button } from '@lidofinance/lido-ui'; import { Zero } from '@ethersproject/constants'; import { Connect, DisabledButton } from 'shared/wallet'; import { FormatToken } from 'shared/formatters/format-token'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useIsSupportedChain } from 'shared/hooks/use-is-supported-chain'; import { isValidationErrorTypeUnhandled } from 'shared/hook-form/validation/validation-error'; import { ClaimFormInputType, useClaimFormData } from '../claim-form-context'; +import { useDappStatus } from 'modules/web3'; export const SubmitButton = () => { - const { isAccountActiveOnL2 } = useDappStatus(); - const { isConnected } = useAccount(); - const isSupportedChain = useIsSupportedChain(); + const { isSupportedChain, isWalletConnected } = useDappStatus(); const { isSubmitting, isValidating, errors } = useFormState(); const { ethToClaim } = useClaimFormData(); const { selectedRequests } = useClaimFormData(); - if (!isConnected) return ; + if (!isWalletConnected) return ; - if (!isSupportedChain || isAccountActiveOnL2) { + if (!isSupportedChain) { return Claim; } diff --git a/features/withdrawals/claim/wallet/wallet.tsx b/features/withdrawals/claim/wallet/wallet.tsx index cd65a071d..7247247ca 100644 --- a/features/withdrawals/claim/wallet/wallet.tsx +++ b/features/withdrawals/claim/wallet/wallet.tsx @@ -1,35 +1,20 @@ -import { memo } from 'react'; - -import { CHAINS } from '@lido-sdk/constants'; import { Divider } from '@lidofinance/lido-ui'; -import { useSDK } from '@lido-sdk/react'; -import { getConfig } from 'config'; import { WalletWrapperStyled, WalletMyRequests, } from 'features/withdrawals/shared'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useLidoMultichainFallbackCondition } from 'shared/hooks/use-lido-multichain-fallback-condition'; -import { - CardAccount, - CardRow, - Fallback, - LidoMultichainFallback, -} from 'shared/wallet'; -import type { WalletComponentType } from 'shared/wallet/types'; +import { CardAccount, CardRow, Fallback } from 'shared/wallet'; import { WalletAvailableAmount } from './wallet-availale-amount'; import { WalletPendingAmount } from './wallet-pending-amount'; export const WalletComponent = () => { - const { account } = useSDK(); - return ( - + @@ -40,40 +25,10 @@ export const WalletComponent = () => { ); }; -export const ClaimWallet: WalletComponentType = memo((props) => { - const { defaultChain } = getConfig(); - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); - const { showLidoMultichainFallback } = useLidoMultichainFallbackCondition(); - - if (showLidoMultichainFallback) { - return ( - - ); - } - - if (isAccountActiveOnL2) { - return ( - - ); - } - - if (isWalletConnected && !isDappActive) { - return ( - - ); - } - - if (!isDappActive) { - return ; - } - - return ; -}); +export const ClaimWallet = () => { + return ( + + + + ); +}; diff --git a/features/withdrawals/hooks/contract/useClaim.ts b/features/withdrawals/hooks/contract/useClaim.ts index f55dce17f..402d2d7d0 100644 --- a/features/withdrawals/hooks/contract/useClaim.ts +++ b/features/withdrawals/hooks/contract/useClaim.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useAccount } from 'wagmi'; import { useSDK } from '@lido-sdk/react'; @@ -11,8 +10,7 @@ import { useTxModalStagesClaim } from 'features/withdrawals/claim/transaction-mo import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; import { runWithTransactionLogger } from 'utils'; import { isContract } from 'utils/isContract'; -import { sendTx } from 'utils/send-tx'; -import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; +import { sendTx, useTxConfirmation, useDappStatus } from 'modules/web3'; import { useWithdrawalsContract } from './useWithdrawalsContract'; @@ -21,7 +19,7 @@ type Args = { }; export const useClaim = ({ onRetry }: Args) => { - const { address } = useAccount(); + const { address } = useDappStatus(); const { providerWeb3 } = useSDK(); const { contractWeb3 } = useWithdrawalsContract(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); diff --git a/features/withdrawals/hooks/contract/useRequest.ts b/features/withdrawals/hooks/contract/useRequest.ts index e8cc75a5f..2f79317e0 100644 --- a/features/withdrawals/hooks/contract/useRequest.ts +++ b/features/withdrawals/hooks/contract/useRequest.ts @@ -17,7 +17,6 @@ import { GatherPermitSignatureResult, useERC20PermitSignature, } from 'shared/hooks'; -import { useIsMultisig } from 'shared/hooks/useIsMultisig'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; import { useApproveOnL1 } from 'shared/hooks/useApproveOnL1'; import { runWithTransactionLogger } from 'utils'; @@ -26,9 +25,13 @@ import { isContract } from 'utils/isContract'; import { useWithdrawalsContract } from './useWithdrawalsContract'; import { useTxModalStagesRequest } from 'features/withdrawals/request/transaction-modal-request/use-tx-modal-stages-request'; import { useTransactionModal } from 'shared/transaction-modal/transaction-modal'; -import { sendTx } from 'utils/send-tx'; +import { + sendTx, + useTxConfirmation, + useIsMultisig, + useDappStatus, +} from 'modules/web3'; import { overrideWithQAMockBoolean } from 'utils/qa'; -import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; // this encapsulates permit/approval & steth/wsteth flows const useWithdrawalRequestMethods = () => { @@ -195,9 +198,10 @@ export const useWithdrawalRequest = ({ onRetry, }: useWithdrawalRequestParams) => { const { chainId } = useSDK(); + const { address } = useDappStatus(); const withdrawalQueueAddress = getWithdrawalQueueAddress(chainId); - const { connector, address } = useAccount(); + const { connector } = useAccount(); const { isBunker } = useWithdrawals(); const { txModalStages } = useTxModalStagesRequest(); const getRequestMethod = useWithdrawalRequestMethods(); diff --git a/features/withdrawals/hooks/contract/useWithdrawalsContract.ts b/features/withdrawals/hooks/contract/useWithdrawalsContract.ts index f19c23cdb..7aa2429f6 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 { useDappStatus } from 'modules/web3'; export const useWithdrawalsContract = () => { const contractWeb3 = useWithdrawalQueueContractWeb3(); const contractRpc = useWithdrawalQueueContractRPC(); - const { address, chainId } = useAccount(); + const { address } = useDappStatus(); - return { contractWeb3, contractRpc, address, chainId }; + return { contractWeb3, contractRpc, address }; }; diff --git a/features/withdrawals/hooks/contract/useWithdrawalsData.ts b/features/withdrawals/hooks/contract/useWithdrawalsData.ts index 2676f3bba..39240c4e4 100644 --- a/features/withdrawals/hooks/contract/useWithdrawalsData.ts +++ b/features/withdrawals/hooks/contract/useWithdrawalsData.ts @@ -2,6 +2,7 @@ import { useCallback } from 'react'; import { Zero } from '@ethersproject/constants'; import { BigNumber } from 'ethers'; import { useLidoSWR } from '@lido-sdk/react'; +import { useDappStatus } from 'modules/web3'; import { useWithdrawalsContract } from './useWithdrawalsContract'; import { RequestStatus, @@ -81,7 +82,8 @@ const getRequestTimeForWQRequestIds = async ( }; export const useWithdrawalRequests = () => { - const { contractRpc, address, chainId } = useWithdrawalsContract(); + const { chainId } = useDappStatus(); + const { contractRpc, address } = useWithdrawalsContract(); // const { data: currentShareRate } = useLidoShareRate(); const swr = useLidoSWR( diff --git a/features/withdrawals/hooks/useWithdrawTxPrice.ts b/features/withdrawals/hooks/useWithdrawTxPrice.ts index be986487e..027854afb 100644 --- a/features/withdrawals/hooks/useWithdrawTxPrice.ts +++ b/features/withdrawals/hooks/useWithdrawTxPrice.ts @@ -1,10 +1,9 @@ import { useMemo } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useAccount } from 'wagmi'; import { TOKENS } from '@lido-sdk/constants'; -import { useLidoSWR, useSDK } from '@lido-sdk/react'; +import { useLidoSWR } from '@lido-sdk/react'; import { config } from 'config'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; @@ -13,6 +12,7 @@ import { useTxCostInUsd } from 'shared/hooks/txCost'; import { useDebouncedValue } from 'shared/hooks/useDebouncedValue'; import { encodeURLQuery } from 'utils/encodeURLQuery'; import { standardFetcher } from 'utils/standardFetcher'; +import { useDappStatus } from 'modules/web3'; import { useWithdrawalsContract } from './contract/useWithdrawalsContract'; import { RequestStatusClaimable } from '../types/request-status'; @@ -28,7 +28,7 @@ export const useRequestTxPrice = ({ isApprovalFlow, requestCount, }: UseRequestTxPriceOptions) => { - const { chainId } = useSDK(); + const { chainId } = useDappStatus(); const { contractRpc } = useWithdrawalsContract(); const fallback = token === 'STETH' @@ -108,8 +108,8 @@ export const useRequestTxPrice = ({ }; export const useClaimTxPrice = (requests: RequestStatusClaimable[]) => { + const { address, chainId } = useDappStatus(); const { contractRpc } = useWithdrawalsContract(); - const { address, chainId } = useAccount(); const requestCount = requests.length || 1; const debouncedSortedSelectedRequests = useDebouncedValue(requests, 2000); diff --git a/features/withdrawals/request/form/controls/submit-button-request.tsx b/features/withdrawals/request/form/controls/submit-button-request.tsx index fd679a3a7..dca093df9 100644 --- a/features/withdrawals/request/form/controls/submit-button-request.tsx +++ b/features/withdrawals/request/form/controls/submit-button-request.tsx @@ -7,8 +7,7 @@ import { import { SubmitButtonHookForm } from 'shared/hook-form/controls/submit-button-hook-form'; import { useFormState } from 'react-hook-form'; import { isValidationErrorTypeUnhandled } from 'shared/hook-form/validation/validation-error'; -import { useIsMultisig } from 'shared/hooks/useIsMultisig'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus, useIsMultisig } from 'modules/web3'; import { DisabledButton } from 'shared/wallet'; // conditional render breaks useFormState, so it can't be inside SubmitButton @@ -34,14 +33,14 @@ export const SubmitButtonRequest = ({ loading, disabled, }: SubmitButtonRequestProps) => { - const { isAccountActiveOnL2 } = useDappStatus(); + const { isSupportedChain } = useDappStatus(); const { isMultisig } = useIsMultisig(); const { isTokenLocked } = useRequestFormData(); const buttonTitle = isTokenLocked ? `Unlock tokens ${isMultisig ? 'for' : 'and'} withdrawal` : 'Request withdrawal'; - if (isAccountActiveOnL2) { + if (!isSupportedChain) { return Request withdrawal; } 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 e50fe5c2a..b9a890ae9 100644 --- a/features/withdrawals/request/form/controls/token-amount-input-request.tsx +++ b/features/withdrawals/request/form/controls/token-amount-input-request.tsx @@ -10,11 +10,10 @@ import { useTvlMessage } from 'features/withdrawals/hooks/useTvlMessage'; import { trackMatomoEvent } from 'utils/track-matomo-event'; import { TokenAmountInputHookForm } from 'shared/hook-form/controls/token-amount-input-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; export const TokenAmountInputRequest = () => { - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); + const { isWalletConnected, isDappActive } = useDappStatus(); const token = useWatch({ name: 'token' }); const { maxAmount, isTokenLocked } = useRequestFormData(); @@ -22,7 +21,7 @@ export const TokenAmountInputRequest = () => { return ( { - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); + const { isWalletConnected, isDappActive } = useDappStatus(); return ( ); diff --git a/features/withdrawals/request/form/paused-info.tsx b/features/withdrawals/request/form/paused-info.tsx index 486d6814e..e3ace7700 100644 --- a/features/withdrawals/request/form/paused-info.tsx +++ b/features/withdrawals/request/form/paused-info.tsx @@ -1,15 +1,15 @@ -import { useChainId } from 'wagmi'; import { Link } from '@lidofinance/lido-ui'; import { config } from 'config'; import { CHAINS } from 'consts/chains'; import { InfoBoxStyled } from 'features/withdrawals/shared'; +import { useDappStatus } from 'modules/web3'; const LIDO_TWITTER_LINK = 'https://twitter.com/lidofinance'; export const PausedInfo = () => { - const chainId = useChainId(); + const { chainId } = useDappStatus(); const docsSepoliaLink = `${config.docsOrigin}/deployed-contracts/sepolia/`; diff --git a/features/withdrawals/request/form/transaction-info.tsx b/features/withdrawals/request/form/transaction-info.tsx index 1b0b53e8d..bc70aefd7 100644 --- a/features/withdrawals/request/form/transaction-info.tsx +++ b/features/withdrawals/request/form/transaction-info.tsx @@ -4,7 +4,7 @@ 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 { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; 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'; 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 86bddb7ac..4a609cdf0 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 @@ -4,7 +4,7 @@ import { useWithdrawals } from 'features/withdrawals/contexts/withdrawals-contex 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 { useStethBalance, useWstethBalance } from 'modules/web3'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; // Provides all data fetching for form to function 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 904523adc..860aeb2d5 100644 --- a/features/withdrawals/request/request-form-context/use-validation-context.ts +++ b/features/withdrawals/request/request-form-context/use-validation-context.ts @@ -5,7 +5,7 @@ import { } from 'features/withdrawals/withdrawals-constants'; import { useIsLedgerLive } from 'shared/hooks/useIsLedgerLive'; import { useAwaiter } from 'shared/hooks/use-awaiter'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import type { RequestFormDataType, RequestFormValidationAsyncContextType, diff --git a/features/withdrawals/request/wallet/wallet.tsx b/features/withdrawals/request/wallet/wallet.tsx index 1834f6674..a034ac971 100644 --- a/features/withdrawals/request/wallet/wallet.tsx +++ b/features/withdrawals/request/wallet/wallet.tsx @@ -1,22 +1,11 @@ -import { memo } from 'react'; import { useWatch } from 'react-hook-form'; -import { CHAINS, TOKENS } from '@lido-sdk/constants'; +import { TOKENS } from '@lido-sdk/constants'; import { Divider } from '@lidofinance/lido-ui'; -import { useSDK } from '@lido-sdk/react'; -import { getConfig } from 'config'; import { WalletMyRequests } from 'features/withdrawals/shared'; import { WalletWrapperStyled } from 'features/withdrawals/shared'; -import { - CardAccount, - CardRow, - Fallback, - LidoMultichainFallback, -} from 'shared/wallet'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useLidoMultichainFallbackCondition } from 'shared/hooks/use-lido-multichain-fallback-condition'; -import type { WalletComponentType } from 'shared/wallet/types'; +import { CardAccount, CardRow, Fallback } from 'shared/wallet'; import { WalletStethBalance } from './wallet-steth-balance'; import { WalletWstethBalance } from './wallet-wsteth-balance'; @@ -24,7 +13,6 @@ import { WalletMode } from './wallet-mode'; import { RequestFormInputType } from '../request-form-context'; export const WalletComponent = () => { - const { account } = useSDK(); const token = useWatch({ name: 'token' }); const isSteth = token === TOKENS.STETH; @@ -32,7 +20,7 @@ export const WalletComponent = () => { {isSteth ? : } - + @@ -43,43 +31,10 @@ export const WalletComponent = () => { ); }; -export const RequestWallet: WalletComponentType = memo((props) => { - const { defaultChain } = getConfig(); - const { isWalletConnected, isDappActive, isAccountActiveOnL2 } = - useDappStatus(); - const { showLidoMultichainFallback } = useLidoMultichainFallbackCondition(); - - if (showLidoMultichainFallback) { - return ( - - ); - } - - if (isAccountActiveOnL2) { - return ( - - ); - } - - if (isWalletConnected && !isDappActive) { - return ( - - ); - } - - if (!isDappActive) { - return ; - } - - return ; -}); +export const RequestWallet = () => { + return ( + + + + ); +}; diff --git a/features/wsteth/shared/hooks/use-debounced-wsteth-steth.ts b/features/wsteth/shared/hooks/use-debounced-wsteth-steth.ts index 00f751042..7abbdcb58 100644 --- a/features/wsteth/shared/hooks/use-debounced-wsteth-steth.ts +++ b/features/wsteth/shared/hooks/use-debounced-wsteth-steth.ts @@ -3,9 +3,8 @@ import type { BigNumber } from 'ethers'; import { useDebouncedValue } from 'shared/hooks/useDebouncedValue'; import { useStethByWsteth } from 'shared/hooks/useStethByWsteth'; -import { useStETHByWstETHOnL2 } from 'shared/hooks/use-stETH-by-wstETH-on-l2'; +import { useStETHByWstETHOnL2, useWstETHByStETHOnL2 } from 'modules/web3'; import { useWstethBySteth } from 'shared/hooks/useWstethBySteth'; -import { useWstETHByStETHOnL2 } from 'shared/hooks/use-wstETH-by-stETH-on-l2'; export const useDebouncedWstethBySteth = ( amount: BigNumber | null, diff --git a/features/wsteth/shared/wallet/wallet.tsx b/features/wsteth/shared/wallet/wallet.tsx index bc0729359..d77d195c0 100644 --- a/features/wsteth/shared/wallet/wallet.tsx +++ b/features/wsteth/shared/wallet/wallet.tsx @@ -1,72 +1,53 @@ -import { memo } from 'react'; -import { useAccount } from 'wagmi'; -import { useConnectorInfo } from 'reef-knot/core-react'; - import { Divider, Text } from '@lidofinance/lido-ui'; -import { useSDK } from '@lido-sdk/react'; -import { config, useConfig } from 'config'; -import { CHAINS } from 'consts/chains'; import { FormatToken } from 'shared/formatters'; import { TokenToWallet } from 'shared/components'; import { useWstethBySteth, useStethByWsteth } from 'shared/hooks'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useLidoMultichainFallbackCondition } from 'shared/hooks/use-lido-multichain-fallback-condition'; -import type { WalletComponentType } from 'shared/wallet/types'; -import { - CardBalance, - CardRow, - CardAccount, - Fallback, - LidoMultichainFallback, -} from 'shared/wallet'; import { + useDappStatus, useEthereumBalance, useStethBalance, useWstethBalance, -} from 'shared/hooks/use-balance'; -import { OPTIMISM, useDappChain } from 'providers/dapp-chain'; -import { capitalizeFirstLetter } from 'utils/capitalize-string'; + useStETHByWstETHOnL2, + useWstETHByStETHOnL2, + DAPP_CHAIN_TYPE, +} from 'modules/web3'; +import { CardBalance, CardRow, CardAccount, Fallback } from 'shared/wallet'; import { StyledCard } from './styles'; -import { useStETHByWstETHOnL2 } from 'shared/hooks/use-stETH-by-wstETH-on-l2'; -import { useWstETHByStETHOnL2 } from 'shared/hooks/use-wstETH-by-stETH-on-l2'; import { useIsLedgerLive } from 'shared/hooks/useIsLedgerLive'; +import { useConfig } from 'config'; +import { useConnectorInfo } from 'reef-knot/core-react'; -const WalletComponent: WalletComponentType = (props) => { - const { account } = useSDK(); - const { isAccountActiveOnL2, isDappActiveOnL2 } = useDappStatus(); +const WalletComponent = () => { + const { isDappActiveOnL2 } = useDappStatus(); const ethBalance = useEthereumBalance(); const stethBalance = useStethBalance(); const wstethBalance = useWstethBalance(); // TODO merge those hooks and only fetch current chain const wstethByStethOnL1 = useWstethBySteth( - !isAccountActiveOnL2 && stethBalance.data ? stethBalance.data : undefined, + !isDappActiveOnL2 && stethBalance.data ? stethBalance.data : undefined, ); const wstethByStethOnL2 = useWstETHByStETHOnL2( - isAccountActiveOnL2 && stethBalance.data ? stethBalance.data : undefined, + isDappActiveOnL2 && stethBalance.data ? stethBalance.data : undefined, ); - const wstethBySteth = isAccountActiveOnL2 + const wstethBySteth = isDappActiveOnL2 ? wstethByStethOnL2 : wstethByStethOnL1; const stethByWstethOnL1 = useStethByWsteth( - !isAccountActiveOnL2 && wstethBalance.data ? wstethBalance.data : undefined, + !isDappActiveOnL2 && wstethBalance.data ? wstethBalance.data : undefined, ); const stethByWstethOnL2 = useStETHByWstETHOnL2( - isAccountActiveOnL2 && wstethBalance.data ? wstethBalance.data : undefined, + isDappActiveOnL2 && wstethBalance.data ? wstethBalance.data : undefined, ); - const stethByWsteth = isAccountActiveOnL2 + const stethByWsteth = isDappActiveOnL2 ? stethByWstethOnL2 : stethByWstethOnL1; return ( - + { /> } /> - + @@ -140,46 +121,31 @@ const WalletComponent: WalletComponentType = (props) => { ); }; -export const Wallet: WalletComponentType = memo((props) => { +type WrapWalletProps = { + isUnwrapMode: boolean; +}; + +export const Wallet = ({ isUnwrapMode }: WrapWalletProps) => { const isLedgerLive = useIsLedgerLive(); const { isLedger: isLedgerHardware } = useConnectorInfo(); const { featureFlags } = useConfig().externalConfig; - const { chainId } = useAccount(); - const { isDappActive, isDappActiveOnL2 } = useDappStatus(); - const { showLidoMultichainFallback } = useLidoMultichainFallbackCondition(); - const { chainName, isMatchDappChainAndWalletChain } = useDappChain(); + const { chainType } = useDappStatus(); - if (!featureFlags.ledgerLiveL2 && isLedgerLive && chainName === OPTIMISM) { - const error = `Optimism is currently not supported in Ledger Live.`; - return ; - } + const isLedgerLiveOptimism = + !featureFlags.ledgerLiveL2 && + isLedgerLive && + chainType === DAPP_CHAIN_TYPE.Optimism; + const isLedgerHardwareOptimism = + isLedgerHardware && chainType === DAPP_CHAIN_TYPE.Optimism; - if (isLedgerHardware && chainName === OPTIMISM) { - const error = `Optimism is currently not supported in Ledger Hardware.`; - return ; + if (isLedgerLiveOptimism || isLedgerHardwareOptimism) { + const error = `Optimism is currently not supported in ${isLedgerLiveOptimism ? 'Ledger Live' : 'Ledger Hardware'}.`; + return ; } - if (isDappActive && !isMatchDappChainAndWalletChain(chainId)) { - const switchToEthereum = - config.defaultChain === CHAINS.Mainnet - ? 'Ethereum' - : capitalizeFirstLetter(CHAINS[config.defaultChain]); - - const switchToOptimism = - config.supportedChains.indexOf(CHAINS.Optimism) > -1 - ? capitalizeFirstLetter(OPTIMISM) - : 'Optimism Sepolia'; - const error = `Wrong network. Please switch to ${chainName === OPTIMISM ? switchToOptimism : switchToEthereum} in your wallet to wrap/unwrap.`; - return ; - } - - if (!isDappActiveOnL2 && showLidoMultichainFallback) { - return ; - } - - if (!isDappActive) { - return ; - } - - return ; -}); + return ( + + + + ); +}; diff --git a/features/wsteth/shared/wrap-faq/wrap-faq.tsx b/features/wsteth/shared/wrap-faq/wrap-faq.tsx index c4bd67600..347a390b3 100644 --- a/features/wsteth/shared/wrap-faq/wrap-faq.tsx +++ b/features/wsteth/shared/wrap-faq/wrap-faq.tsx @@ -1,7 +1,6 @@ import { Section } from 'shared/components'; import { useMatomoEventHandle } from 'shared/hooks'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useDappChain, OPTIMISM } from 'providers/dapp-chain'; +import { useDappStatus, DAPP_CHAIN_TYPE } from 'modules/web3'; import { WhatIsWsteth, @@ -25,11 +24,10 @@ import { } from './optimism-list'; export const WrapFaq = () => { - const { chainName } = useDappChain(); - const { isWalletConnected } = useDappStatus(); + const { isWalletConnected, chainType } = useDappStatus(); const onClickHandler = useMatomoEventHandle(); - if (isWalletConnected && chainName === OPTIMISM) { + if (isWalletConnected && chainType === DAPP_CHAIN_TYPE.Optimism) { return (
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 22e96dfa0..55db466c0 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 { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useAwaiter } from 'shared/hooks/use-awaiter'; import type { UnwrapFormNetworkData } from '../unwrap-form-context'; diff --git a/features/wsteth/unwrap/hooks/use-unwrap-form-network-data.ts b/features/wsteth/unwrap/hooks/use-unwrap-form-network-data.ts index 6bbe9e9a4..5e788b744 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-form-network-data.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-form-network-data.ts @@ -1,6 +1,5 @@ import { useCallback, useMemo } from 'react'; -import { useIsMultisig } from 'shared/hooks/useIsMultisig'; -import { useStethBalance, useWstethBalance } from 'shared/hooks/use-balance'; +import { useStethBalance, useWstethBalance, useIsMultisig } from 'modules/web3'; export const useUnwrapFormNetworkData = () => { const { isMultisig } = useIsMultisig(); diff --git a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts index 46ce7eb43..e77d4bff6 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-form-processing.ts @@ -2,7 +2,6 @@ import { useCallback } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useAccount } from 'wagmi'; import { useSDK, @@ -12,13 +11,15 @@ import { import { TransactionCallbackStage } from '@lidofinance/lido-ethereum-sdk/core'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; -import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; +import { + useTxConfirmation, + sendTx, + useLidoSDK, + useDappStatus, + useGetIsContract, +} from 'modules/web3'; import { runWithTransactionLogger } from 'utils'; -import { sendTx } from 'utils/send-tx'; -import { useLidoSDK } from 'providers/lido-sdk'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useGetIsContract } from 'shared/hooks/use-is-contract'; import { convertToBigNumber } from 'utils/convert-to-big-number'; import type { UnwrapFormInputType } from '../unwrap-form-context'; @@ -38,7 +39,7 @@ export const useUnwrapFormProcessor = ({ onConfirm, onRetry, }: UseUnwrapFormProcessorArgs) => { - const { address } = useAccount(); + const { isDappActiveOnL2, address } = useDappStatus(); const { providerWeb3 } = useSDK(); const { staticRpcProvider } = useCurrentStaticRpcProvider(); const { txModalStages } = useTxModalStagesUnwrap(); @@ -47,7 +48,6 @@ export const useUnwrapFormProcessor = ({ const waitForTx = useTxConfirmation(); const isContract = useGetIsContract(); const { l2, stETH, isL2 } = useLidoSDK(); - const { isAccountActiveOnL2 } = useDappStatus(); const { isApprovalNeededBeforeUnwrap: isApprovalNeededBeforeUnwrapOnL2, @@ -134,9 +134,7 @@ export const useUnwrapFormProcessor = ({ ); const [stethBalance] = await Promise.all([ - isAccountActiveOnL2 - ? l2.steth.balance(address) - : stETH.balance(address), + isDappActiveOnL2 ? l2.steth.balance(address) : stETH.balance(address), onConfirm(), ]); @@ -156,7 +154,7 @@ export const useUnwrapFormProcessor = ({ wstETHContractRPC, isApprovalNeededBeforeUnwrapOnL2, txModalStages, - isAccountActiveOnL2, + isDappActiveOnL2, stETH, onConfirm, providerWeb3, diff --git a/features/wsteth/unwrap/hooks/use-unwrap-gas-limit.ts b/features/wsteth/unwrap/hooks/use-unwrap-gas-limit.ts index b1f870a4d..c03ae0103 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-gas-limit.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-gas-limit.ts @@ -3,8 +3,7 @@ import { useLidoSWR } from '@lido-sdk/react'; import { config } from 'config'; import { UNWRAP_GAS_LIMIT, UNWRAP_L2_GAS_LIMIT } from 'consts/tx'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; -import { useLidoSDK } from 'providers/lido-sdk'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useLidoSDK, useDappStatus } from 'modules/web3'; import { BigNumber } from 'ethers'; export const useUnwrapGasLimit = () => { diff --git a/features/wsteth/unwrap/hooks/use-unwrap-tx-on-l2-approve.ts b/features/wsteth/unwrap/hooks/use-unwrap-tx-on-l2-approve.ts index f4db183d9..7be838b02 100644 --- a/features/wsteth/unwrap/hooks/use-unwrap-tx-on-l2-approve.ts +++ b/features/wsteth/unwrap/hooks/use-unwrap-tx-on-l2-approve.ts @@ -2,10 +2,8 @@ import { useMemo, useCallback } from 'react'; import type { BigNumber } from 'ethers'; import { runWithTransactionLogger } from 'utils'; -import { useLidoSDK } from 'providers/lido-sdk'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useAllowance } from 'shared/hooks/use-allowance'; -import { useAccount } from 'wagmi'; +import { useLidoSDK, useDappStatus, useAllowance } from 'modules/web3'; + import { LIDO_L2_CONTRACT_ADDRESSES } from '@lidofinance/lido-ethereum-sdk/common'; import { TransactionCallbackStage } from '@lidofinance/lido-ethereum-sdk/core'; @@ -14,8 +12,7 @@ type UseUnwrapTxApproveArgs = { }; export const useUnwrapTxOnL2Approve = ({ amount }: UseUnwrapTxApproveArgs) => { - const { address } = useAccount(); - const { isAccountActiveOnL2 } = useDappStatus(); + const { isDappActiveOnL2, isChainTypeOnL2, address } = useDappStatus(); const { core, l2 } = useLidoSDK(); const staticTokenAddress = LIDO_L2_CONTRACT_ADDRESSES[core.chainId]?.wsteth; @@ -27,7 +24,7 @@ export const useUnwrapTxOnL2Approve = ({ amount }: UseUnwrapTxApproveArgs) => { refetch: refetchAllowance, isLoading: isAllowanceLoading, } = useAllowance({ - account: isAccountActiveOnL2 ? address : undefined, + account: isDappActiveOnL2 ? address : undefined, spender: staticSpenderAddress, token: staticTokenAddress, }); @@ -63,15 +60,19 @@ export const useUnwrapTxOnL2Approve = ({ amount }: UseUnwrapTxApproveArgs) => { allowance, isApprovalNeededBeforeUnwrap, isAllowanceLoading, - isShowAllowance: isAccountActiveOnL2, + // There are 2 cases when we show the allowance on the unwrap page: + // 1. wallet chain is any Optimism supported chain and chain switcher is Optimism (isDappActiveOnL2) + // 2. or wallet chain is any ETH supported chain, but chain switcher is Optimism (isChainTypeOnL2) + isShowAllowance: isDappActiveOnL2 || isChainTypeOnL2, }), [ processApproveTx, refetchAllowance, - isAllowanceLoading, allowance, isApprovalNeededBeforeUnwrap, - isAccountActiveOnL2, + isAllowanceLoading, + isDappActiveOnL2, + isChainTypeOnL2, ], ); }; diff --git a/features/wsteth/unwrap/unwrap-form-controls/amount-input-unwrap.tsx b/features/wsteth/unwrap/unwrap-form-controls/amount-input-unwrap.tsx index fe6558f7c..3a7f9cd88 100644 --- a/features/wsteth/unwrap/unwrap-form-controls/amount-input-unwrap.tsx +++ b/features/wsteth/unwrap/unwrap-form-controls/amount-input-unwrap.tsx @@ -2,7 +2,7 @@ import { Wsteth } from '@lidofinance/lido-ui'; import { TOKENS } from '@lido-sdk/constants'; import { TokenAmountInputHookForm } from 'shared/hook-form/controls/token-amount-input-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useUnwrapFormData } from '../unwrap-form-context'; diff --git a/features/wsteth/unwrap/unwrap-form/unwrap-stats.tsx b/features/wsteth/unwrap/unwrap-form/unwrap-stats.tsx index e27cb6ef8..e0115c268 100644 --- a/features/wsteth/unwrap/unwrap-form/unwrap-stats.tsx +++ b/features/wsteth/unwrap/unwrap-form/unwrap-stats.tsx @@ -8,7 +8,7 @@ import { AllowanceDataTableRow } from 'shared/components/allowance-data-table-ro import { FormatToken } from 'shared/formatters/format-token'; import { FormatPrice } from 'shared/formatters'; import { useTxCostInUsd } from 'shared/hooks'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useDebouncedStethByWsteth } from 'features/wsteth/shared/hooks/use-debounced-wsteth-steth'; import { useUnwrapGasLimit } from '../hooks/use-unwrap-gas-limit'; @@ -16,28 +16,32 @@ import { useUnwrapFormData, UnwrapFormInputType } from '../unwrap-form-context'; import { useApproveGasLimit } from 'features/wsteth/wrap/hooks/use-approve-gas-limit'; export const UnwrapStats = () => { - const { - isWalletConnected, - isAccountActiveOnL2, - isDappActiveAndNetworksMatched, - } = useDappStatus(); + const { isDappActiveOnL2, chainTypeChainId } = useDappStatus(); const { allowance, isAllowanceLoading, isShowAllowance } = useUnwrapFormData(); const amount = useWatch({ name: 'amount' }); + const unwrapGasLimit = useUnwrapGasLimit(); + // The 'unwrapGasLimit' difference between the networks is insignificant + // and can be neglected in the '!isChainTypeMatched' case + // + // Using the chainTypeChainId (chainId from the chain switcher) for TX calculation (and below for 'approveTxCostInUsd'), + // because the statistics here are shown for the chain from the chain switcher const { txCostUsd: unwrapTxCostInUsd, initialLoading: isUnwrapTxCostLoading, - } = useTxCostInUsd(unwrapGasLimit); + } = useTxCostInUsd(unwrapGasLimit, chainTypeChainId); const approveGasLimit = useApproveGasLimit(); + // The 'approveGasLimit' difference between the networks is insignificant + // and can be neglected in the '!isChainTypeMatched' case const { txCostUsd: approveTxCostInUsd, initialLoading: isApproveCostLoading, - } = useTxCostInUsd(approveGasLimit); + } = useTxCostInUsd(approveGasLimit, chainTypeChainId); const { data: willReceiveStETH, initialLoading: isWillReceiveStETHLoading } = - useDebouncedStethByWsteth(amount, isAccountActiveOnL2); + useDebouncedStethByWsteth(amount, isDappActiveOnL2); return ( @@ -58,11 +62,7 @@ export const UnwrapStats = () => { data-testid="maxGasFee" loading={isUnwrapTxCostLoading} > - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : ( - - )} + {isShowAllowance && ( { data-testid="maxUnlockFee" loading={isApproveCostLoading} > - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : ( - - )} + )} @@ -82,6 +78,7 @@ export const UnwrapStats = () => { diff --git a/features/wsteth/wrap-unwrap-tabs.tsx b/features/wsteth/wrap-unwrap-tabs.tsx index 54cc5faff..dfa3e6ce9 100644 --- a/features/wsteth/wrap-unwrap-tabs.tsx +++ b/features/wsteth/wrap-unwrap-tabs.tsx @@ -24,7 +24,7 @@ export const WrapUnwrapTabs = ({ mode }: WrapUnwrapLayoutProps) => { <> - + {isUnwrapMode ? : } }> diff --git a/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx b/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx index c3f60d6aa..aff185774 100644 --- a/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx +++ b/features/wsteth/wrap/hooks/use-approve-gas-limit.tsx @@ -7,8 +7,7 @@ import { WSTETH_APPROVE_GAS_LIMIT, } from 'consts/tx'; import { STRATEGY_LAZY } from 'consts/swr-strategies'; -import { useLidoSDK } from 'providers/lido-sdk'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useLidoSDK, useDappStatus } from 'modules/web3'; export const useApproveGasLimit = () => { const { isDappActiveOnL2 } = useDappStatus(); 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 b0da4caf7..0ab1cd4eb 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-network-data.ts @@ -1,16 +1,17 @@ import { useCallback, useMemo } from 'react'; import { config } from 'config'; -import { useIsMultisig } from 'shared/hooks/useIsMultisig'; import { useTokenMaxAmount } from 'shared/hooks/use-token-max-amount'; -import { useMaxGasPrice, useStakingLimitInfo } from 'shared/hooks'; +import { useStakingLimitInfo } from 'shared/hooks'; import { useWrapGasLimit } from './use-wrap-gas-limit'; import { useEthereumBalance, useStethBalance, useWstethBalance, -} from 'shared/hooks/use-balance'; + useMaxGasPrice, + useIsMultisig, +} from 'modules/web3'; // Provides all data fetching for form to function export const useWrapFormNetworkData = () => { diff --git a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts index 97d669fc4..213fcb4b8 100644 --- a/features/wsteth/wrap/hooks/use-wrap-form-processing.ts +++ b/features/wsteth/wrap/hooks/use-wrap-form-processing.ts @@ -1,19 +1,20 @@ import { useCallback } from 'react'; import { BigNumber } from 'ethers'; import invariant from 'tiny-invariant'; -import { useAccount } from 'wagmi'; import { useSDK, useWSTETHContractRPC } from '@lido-sdk/react'; import { TransactionCallbackStage } from '@lidofinance/lido-ethereum-sdk/core'; -import { useTxConfirmation } from 'shared/hooks/use-tx-conformation'; -import { useGetIsContract } from 'shared/hooks/use-is-contract'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { + useTxConfirmation, + useGetIsContract, + useDappStatus, + useLidoSDK, +} from 'modules/web3'; + import { runWithTransactionLogger } from 'utils'; import { convertToBigNumber } from 'utils/convert-to-big-number'; -import { useLidoSDK } from 'providers/lido-sdk'; - import type { WrapFormApprovalData, WrapFormInputType, @@ -32,13 +33,11 @@ export const useWrapFormProcessor = ({ onConfirm, onRetry, }: UseWrapFormProcessorArgs) => { - const { address } = useAccount(); + const { isDappActiveOnL2, address } = useDappStatus(); const { providerWeb3 } = useSDK(); const wstETHContractRPC = useWSTETHContractRPC(); const { l2, isL2, wstETH } = useLidoSDK(); - const { isAccountActiveOnL2 } = useDappStatus(); - const { txModalStages } = useTxModalWrap(); const processWrapTxOnL1 = useWrapTxOnL1Processing(); @@ -60,7 +59,7 @@ export const useWrapFormProcessor = ({ const [isMultisig, willReceive] = await Promise.all([ isContract(address), - isAccountActiveOnL2 + isDappActiveOnL2 ? l2.steth .convertToShares(amount.toBigInt()) .then(convertToBigNumber) @@ -86,7 +85,7 @@ export const useWrapFormProcessor = ({ txModalStages.sign(amount, token, willReceive); let txHash: string; - if (isAccountActiveOnL2) { + if (isDappActiveOnL2) { const txResult = await runWithTransactionLogger( 'Wrap signing on L2', () => @@ -118,7 +117,7 @@ export const useWrapFormProcessor = ({ ); const [wstethBalance] = await Promise.all([ - isAccountActiveOnL2 + isDappActiveOnL2 ? l2.wsteth.balance(address) : wstETH.balance(address), onConfirm(), @@ -136,7 +135,7 @@ export const useWrapFormProcessor = ({ isL2, address, isContract, - isAccountActiveOnL2, + isDappActiveOnL2, l2, wstETHContractRPC, isApprovalNeededBeforeWrapOnL1, 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 e044da344..ce4870559 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 { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useAwaiter } from 'shared/hooks/use-awaiter'; import type { diff --git a/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts b/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts index 26e792618..b0d8ebdea 100644 --- a/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts +++ b/features/wsteth/wrap/hooks/use-wrap-gas-limit.ts @@ -10,17 +10,17 @@ import { applyGasLimitRatio, applyGasLimitRatioBigInt, } from 'utils/apply-gas-limit-ratio'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; -import { useLidoSDK } from 'providers/lido-sdk'; +import { useDappStatus, useLidoSDK } from 'modules/web3'; + import { ESTIMATE_ACCOUNT, ESTIMATE_AMOUNT } from 'config/groups/web3'; import { BigNumber } from 'ethers'; import { Zero } from '@ethersproject/constants'; export const useWrapGasLimit = () => { - const { isAccountActiveOnL2 } = useDappStatus(); + const { isDappActiveOnL2 } = useDappStatus(); const { l2, isL2, wrap, core } = useLidoSDK(); - const wrapFallback = isAccountActiveOnL2 ? WRAP_L2_GAS_LIMIT : WRAP_GAS_LIMIT; + const wrapFallback = isDappActiveOnL2 ? WRAP_L2_GAS_LIMIT : WRAP_GAS_LIMIT; const { data } = useLidoSWR( ['[swr:wrap-gas-limit]', core.chainId, isL2], diff --git a/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-approve.ts b/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-approve.ts index 4211e1f74..2173ceb51 100644 --- a/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-approve.ts +++ b/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-approve.ts @@ -6,7 +6,7 @@ import { useSDK } from '@lido-sdk/react'; import { TokensWrappable, TOKENS_TO_WRAP } from 'features/wsteth/shared/types'; import { useApproveOnL1 } from 'shared/hooks/useApproveOnL1'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; type UseWrapTxApproveArgs = { amount: BigNumber; @@ -17,7 +17,8 @@ export const useWrapTxOnL1Approve = ({ amount, token, }: UseWrapTxApproveArgs) => { - const { isDappActiveOnL1 } = useDappStatus(); + const { isWalletConnected, isDappActiveOnL1, isChainTypeOnL2 } = + useDappStatus(); const { chainId } = useSDK(); const [stethTokenAddress, wstethTokenAddress] = useMemo( @@ -51,16 +52,23 @@ export const useWrapTxOnL1Approve = ({ isApprovalLoading, isApprovalNeededBeforeWrap, refetchAllowance, - isShowAllowance: isDappActiveOnL1, + // There are 3 cases when we show the allowance on the wrap page: + // 1. is wallet not connected (!isWalletConnected) + // 2. or wallet chain is any ETH supported chain and chain switcher is ETH (isDappActiveOnL1) + // 3. or wallet chain is any Optimism supported chain, but chain switcher is ETH (!isChainTypeOnL2) + isShowAllowance: + !isWalletConnected || isDappActiveOnL1 || !isChainTypeOnL2, }), [ - allowance, - isApprovalNeededBeforeWrap, + processApproveTx, needsApprove, + allowance, isApprovalLoading, - processApproveTx, + isApprovalNeededBeforeWrap, refetchAllowance, + isWalletConnected, isDappActiveOnL1, + isChainTypeOnL2, ], ); }; diff --git a/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-processing.ts b/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-processing.ts index 7ec7eadb6..629b7df49 100644 --- a/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-processing.ts +++ b/features/wsteth/wrap/hooks/use-wrap-tx-on-l1-processing.ts @@ -3,28 +3,16 @@ import invariant from 'tiny-invariant'; import { useSDK, useWSTETHContractWeb3 } from '@lido-sdk/react'; import { TOKENS } from '@lido-sdk/constants'; -import { StaticJsonRpcBatchProvider } from '@lidofinance/eth-providers'; import { config } from 'config'; import { MockLimitReachedError } from 'features/stake/stake-form/utils'; import { useCurrentStaticRpcProvider } from 'shared/hooks/use-current-static-rpc-provider'; -import { getFeeData } from 'utils/getFeeData'; import type { WrapFormInputType } from '../wrap-form-context'; -import { sendTx } from 'utils/send-tx'; +import { sendTx } from 'modules/web3'; import { PopulatedTransaction } from 'ethers'; -export const getGasParameters = async ( - provider: StaticJsonRpcBatchProvider, -) => { - const { maxFeePerGas, maxPriorityFeePerGas } = await getFeeData(provider); - return { - maxPriorityFeePerGas, - maxFeePerGas, - }; -}; - type WrapTxProcessorArgs = WrapFormInputType & { isMultisig: boolean; }; diff --git a/features/wsteth/wrap/wrap-form-controls/input-group-wrap.tsx b/features/wsteth/wrap/wrap-form-controls/input-group-wrap.tsx index 8e5517441..82e4be9d4 100644 --- a/features/wsteth/wrap/wrap-form-controls/input-group-wrap.tsx +++ b/features/wsteth/wrap/wrap-form-controls/input-group-wrap.tsx @@ -1,7 +1,6 @@ import { useWatch } from 'react-hook-form'; import { InputGroupHookForm } from 'shared/hook-form/controls/input-group-hook-form'; -import { useStakingLimitWarning } from 'shared/hooks/use-staking-limit-warning'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useStakingLimitWarning, useDappStatus } from 'modules/web3'; import { WrapFormInputType, useWrapFormData } from '../wrap-form-context'; import { TokenAmountInputWrap } from './token-amount-input-wrap'; diff --git a/features/wsteth/wrap/wrap-form-controls/token-amount-input-wrap.tsx b/features/wsteth/wrap/wrap-form-controls/token-amount-input-wrap.tsx index 8f65cb553..0bb1897eb 100644 --- a/features/wsteth/wrap/wrap-form-controls/token-amount-input-wrap.tsx +++ b/features/wsteth/wrap/wrap-form-controls/token-amount-input-wrap.tsx @@ -2,7 +2,7 @@ import { Steth } from '@lidofinance/lido-ui'; import { useWatch } from 'react-hook-form'; import { TokenAmountInputHookForm } from 'shared/hook-form/controls/token-amount-input-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useWrapFormData, WrapFormInputType } from '../wrap-form-context'; @@ -12,28 +12,20 @@ type TokenAmountInputWrapProps = Pick< >; export const TokenAmountInputWrap = (props: TokenAmountInputWrapProps) => { - const { - isWalletConnected, - isAccountActiveOnL2, - isDappActiveAndNetworksMatched, - } = useDappStatus(); + const { isWalletConnected, isDappActiveOnL2, isDappActive } = useDappStatus(); const token = useWatch({ name: 'token' }); const { maxAmount, isApprovalNeededBeforeWrap } = useWrapFormData(); return ( - ) : undefined - } + leftDecorator={isDappActiveOnL2 ? : undefined} {...props} /> ); diff --git a/features/wsteth/wrap/wrap-form-controls/token-select-wrap.tsx b/features/wsteth/wrap/wrap-form-controls/token-select-wrap.tsx index d77a9ff49..2f0d8b32c 100644 --- a/features/wsteth/wrap/wrap-form-controls/token-select-wrap.tsx +++ b/features/wsteth/wrap/wrap-form-controls/token-select-wrap.tsx @@ -4,7 +4,7 @@ import { trackEvent } from '@lidofinance/analytics-matomo'; import { TOKENS_TO_WRAP } from 'features/wsteth/shared/types'; import { MATOMO_CLICK_EVENTS } from 'consts/matomo-click-events'; import { TokenSelectHookForm } from 'shared/hook-form/controls/token-select-hook-form/token-select-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; const OPTION_STETH = { label: 'Lido (stETH)', @@ -22,11 +22,7 @@ type TokenSelectWrapProps = Pick< >; export const TokenSelectWrap = (props: TokenSelectWrapProps) => { - const { - isWalletConnected, - isDappActiveAndNetworksMatched, - isDappActiveOnL2, - } = useDappStatus(); + const { isWalletConnected, isDappActive, isDappActiveOnL2 } = useDappStatus(); const options = useMemo(() => { if (isDappActiveOnL2) { @@ -38,7 +34,7 @@ export const TokenSelectWrap = (props: TokenSelectWrapProps) => { return ( { trackEvent( diff --git a/features/wsteth/wrap/wrap-form/wrap-stats.tsx b/features/wsteth/wrap/wrap-form/wrap-stats.tsx index 900bcb670..7b40168b2 100644 --- a/features/wsteth/wrap/wrap-form/wrap-stats.tsx +++ b/features/wsteth/wrap/wrap-form/wrap-stats.tsx @@ -10,8 +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 { useWstETHByStETHOnL2 } from 'shared/hooks/use-wstETH-by-stETH-on-l2'; +import { useDappStatus, useWstETHByStETHOnL2 } from 'modules/web3'; import { useApproveGasLimit } from '../hooks/use-approve-gas-limit'; import { useWrapFormData, WrapFormInputType } from '../wrap-form-context'; @@ -19,12 +18,7 @@ import { useWrapFormData, WrapFormInputType } from '../wrap-form-context'; const oneSteth = parseEther('1'); export const WrapFormStats = () => { - const { - isWalletConnected, - isDappActive, - isDappActiveOnL2, - isDappActiveAndNetworksMatched, - } = useDappStatus(); + const { isDappActive, isDappActiveOnL2, chainTypeChainId } = useDappStatus(); const { allowance, isShowAllowance, wrapGasLimit, isApprovalLoading } = useWrapFormData(); @@ -50,14 +44,21 @@ export const WrapFormStats = () => { initialLoading: oneWstethConvertedLoading, } = isDappActiveOnL2 ? wstETHByStETHOnL2 : wstethBySteth; + // The 'approveGasLimit' difference between the networks is insignificant + // and can be neglected in the '!isChainTypeMatched' case + // + // Using the chainTypeChainId (chainId from the chain switcher) for TX calculation (and below for 'wrapTxCostInUsd'), + // because the statistics here are shown for the chain from the chain switcher const approveGasLimit = useApproveGasLimit(); const { txCostUsd: approveTxCostInUsd, initialLoading: isApproveCostLoading, - } = useTxCostInUsd(approveGasLimit); + } = useTxCostInUsd(approveGasLimit, chainTypeChainId); + // The 'wrapGasLimit' difference between the networks is insignificant + // and can be neglected in the '!isChainTypeMatched' case const { txCostUsd: wrapTxCostInUsd, initialLoading: isWrapCostLoading } = - useTxCostInUsd(wrapGasLimit); + useTxCostInUsd(wrapGasLimit, chainTypeChainId); return ( @@ -73,17 +74,13 @@ export const WrapFormStats = () => { trimEllipsis /> - {(!isDappActive || isShowAllowance) && ( + {isShowAllowance && ( - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : ( - - )} + )} { data-testid="maxGasFee" loading={isWrapCostLoading} > - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : ( - - )} + - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : oneWstethConverted ? ( + {oneWstethConverted ? ( <> 1 {isSteth ? 'stETH' : 'ETH'} ={' '} { DATA_UNAVAILABLE )} - {(!isDappActive || isShowAllowance) && ( + {isShowAllowance && ( => { - return Promise.all( - list.map(async (id) => { - const fileContents = await import(`faq/${id}.md`); - const matterResult = matter(fileContents.default); - - const processedContent = await remark() - .use(externalLinks, { target: '_blank', rel: ['nofollow', 'noopener'] }) - .use(html) - .process(matterResult.content); - - const content = processedContent.toString(); - const title = String(matterResult.data.title || id); - - return { - id, - content, - title, - }; - }), - ); -}; diff --git a/modules/web3/hooks/index.ts b/modules/web3/hooks/index.ts new file mode 100644 index 000000000..c6fb0e3ae --- /dev/null +++ b/modules/web3/hooks/index.ts @@ -0,0 +1,11 @@ +export * from './use-allowance'; +export * from './use-balance'; +export * from './use-dapp-status'; +export * from './use-is-contract'; +export * from './use-tx-conformation'; +export * from './use-wagmi-key'; +export * from './use-max-gas-price'; +export * from './use-is-multisig'; +export * from './use-stETH-by-wstETH-on-l2'; +export * from './use-wstETH-by-stETH-on-l2'; +export * from './use-staking-limit-warning'; diff --git a/shared/hooks/use-allowance.ts b/modules/web3/hooks/use-allowance.ts similarity index 91% rename from shared/hooks/use-allowance.ts rename to modules/web3/hooks/use-allowance.ts index a62d76414..177f34103 100644 --- a/shared/hooks/use-allowance.ts +++ b/modules/web3/hooks/use-allowance.ts @@ -1,9 +1,11 @@ +import { useCallback, useMemo } from 'react'; import { useQueryClient } from '@tanstack/react-query'; import { BigNumber } from 'ethers'; -import { useCallback, useMemo } from 'react'; import { Address, WatchContractEventOnLogsFn } from 'viem'; import { useReadContract, useWatchContractEvent } from 'wagmi'; +import { useDappStatus } from './use-dapp-status'; + const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); const Erc20AllowanceAbi = [ @@ -70,13 +72,16 @@ export const useAllowance = ({ account, spender, }: UseAllowanceProps) => { + const { chainId } = useDappStatus(); + const { isSupportedChain } = useDappStatus(); const queryClient = useQueryClient(); - const enabled = !!(token && account && spender); + const enabled = !!(token && account && spender && isSupportedChain); const allowanceQuery = useReadContract({ abi: Erc20AllowanceAbi, address: token, functionName: 'allowance', + chainId, args: [account, spender] as [Address, Address], query: { enabled, @@ -102,6 +107,7 @@ export const useAllowance = ({ abi: Erc20AllowanceAbi, eventName: 'Approval', poll: true, + chainId, args: useMemo( () => ({ owner: account, @@ -119,6 +125,7 @@ export const useAllowance = ({ abi: Erc20AllowanceAbi, eventName: 'Transfer', poll: true, + chainId, args: useMemo( () => ({ from: account, diff --git a/shared/hooks/use-balance.ts b/modules/web3/hooks/use-balance.ts similarity index 85% rename from shared/hooks/use-balance.ts rename to modules/web3/hooks/use-balance.ts index e29d6ee3d..2339f019a 100644 --- a/shared/hooks/use-balance.ts +++ b/modules/web3/hooks/use-balance.ts @@ -1,60 +1,42 @@ /* 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 type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; -import { BigNumber } from 'ethers'; -import { useLidoSDK } from 'providers/lido-sdk'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import type { Address, WatchContractEventOnLogsFn } from 'viem'; +import { type QueryKey, useQuery, useQueryClient } from '@tanstack/react-query'; import { - useBlockNumber, useBalance, - useAccount, useReadContract, useWatchContractEvent, + useAccount, } from 'wagmi'; -import type { GetBalanceData } from 'wagmi/query'; +import { BigNumber } from 'ethers'; +import { useLidoSDK } from 'modules/web3'; import { config } from 'config'; +import { useDappStatus } from './use-dapp-status'; + +import type { Address, WatchContractEventOnLogsFn } from 'viem'; +import type { GetBalanceData } from 'wagmi/query'; +import type { AbstractLidoSDKErc20 } from '@lidofinance/lido-ethereum-sdk/erc20'; + const nativeToBN = (data: bigint) => BigNumber.from(data.toString()); const balanceToBN = (data: GetBalanceData) => nativeToBN(data.value); export const useEthereumBalance = () => { - const queryClient = useQueryClient(); - const { address } = useAccount(); - const { data: blockNumber } = useBlockNumber({ - watch: { - poll: true, - pollingInterval: config.PROVIDER_POLLING_INTERVAL, - enabled: !!address, - }, - cacheTime: config.PROVIDER_POLLING_INTERVAL, - }); + const { chainId, address, isDappActive } = useDappStatus(); const queryData = useBalance({ address, + chainId, query: { select: balanceToBN, - // because we subscribe to block - staleTime: Infinity, - enabled: !!address, + staleTime: config.PROVIDER_POLLING_INTERVAL, + refetchInterval: config.PROVIDER_POLLING_INTERVAL, + enabled: isDappActive, }, }); - 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 }, - ); - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [blockNumber]); - return queryData; }; @@ -217,15 +199,19 @@ const useTokenBalance = ( address?: Address, shouldSubscribe = true, ) => { + const { isDappActive, chainId } = useDappStatus(); const { subscribeToTokenUpdates } = useLidoSDK(); + const enabled = !!address && isDappActive; + const balanceQuery = useReadContract({ abi: contract?.abi, address: contract?.address, + chainId, functionName: 'balanceOf', args: address && [address], query: { - enabled: !!address, + enabled, select: nativeToBN, // because we update on events we can have high staleTime // this prevents loader when changing pages @@ -234,7 +220,7 @@ const useTokenBalance = ( }); useEffect(() => { - if (shouldSubscribe && address && contract?.address) { + if (shouldSubscribe && enabled && address && contract?.address) { return subscribeToTokenUpdates({ tokenAddress: contract.address, queryKey: balanceQuery.queryKey, @@ -242,7 +228,7 @@ const useTokenBalance = ( } // queryKey causes rerender // eslint-disable-next-line react-hooks/exhaustive-deps - }, [address, contract?.address]); + }, [address, enabled, contract?.address]); return balanceQuery; }; @@ -252,13 +238,13 @@ export const useStethBalance = ({ shouldSubscribeToUpdates = true, }: UseBalanceProps = {}) => { const { core, l2, stETH, isL2 } = useLidoSDK(); - const { address } = useAccount(); + const { isSupportedChain, address } = useDappStatus(); const mergedAccount = account ?? address; const { data: contract, isLoading } = useQuery({ queryKey: ['steth-contract', core.chainId, isL2], - enabled: !!mergedAccount, + enabled: !!mergedAccount && isSupportedChain, staleTime: Infinity, queryFn: async () => (isL2 ? l2.steth.getContract() : stETH.getContract()), @@ -281,7 +267,7 @@ export const useWstethBalance = ({ account, shouldSubscribeToUpdates = true, }: UseBalanceProps = {}) => { - const { address } = useAccount(); + const { isSupportedChain, address } = useDappStatus(); const mergedAccount = account ?? address; const { core: lidoSDKCore, @@ -292,7 +278,7 @@ export const useWstethBalance = ({ const { data: contract, isLoading } = useQuery({ queryKey: ['wsteth-contract', lidoSDKCore.chainId, isL2], - enabled: !!mergedAccount, + enabled: !!mergedAccount && isSupportedChain, staleTime: Infinity, queryFn: async () => isL2 ? lidoSDKL2.wsteth.getContract() : lidoSDKwstETH.getContract(), diff --git a/modules/web3/hooks/use-dapp-status.ts b/modules/web3/hooks/use-dapp-status.ts new file mode 100644 index 000000000..742fbf03e --- /dev/null +++ b/modules/web3/hooks/use-dapp-status.ts @@ -0,0 +1,42 @@ +import { useAccount } from 'wagmi'; +import { isSDKSupportedL2Chain } from 'consts/chains'; + +import { useDappChain } from 'modules/web3/web3-provider/dapp-chain'; + +export const useDappStatus = () => { + const { + address, + chainId: walletChainId, + isConnected: isWalletConnected, + } = useAccount(); + + // this can change between pages based on their dapp-chain context(or lack of) + const dappChain = useDappChain(); + + const { isSupportedChain, isChainTypeMatched } = dappChain; + + const isAccountActive = walletChainId + ? isWalletConnected && isSupportedChain + : false; + + const isL2 = isSDKSupportedL2Chain(walletChainId); + + const isDappActive = isAccountActive && isChainTypeMatched; + + const isDappActiveOnL1 = isDappActive && !isL2; + + const isDappActiveOnL2 = isDappActive && isL2; + + // no useMemo because memoisation is more expensive than boolean flags + // hook is used in many places and every usage would create separate memoisation + return { + ...dappChain, + isAccountActive, + isDappActive, + isDappActiveOnL2, + isDappActiveOnL1, + isWalletConnected, + walletChainId, + address, + }; +}; diff --git a/shared/hooks/use-is-contract.ts b/modules/web3/hooks/use-is-contract.ts similarity index 79% rename from shared/hooks/use-is-contract.ts rename to modules/web3/hooks/use-is-contract.ts index b0c4c1eb3..4163ceba1 100644 --- a/shared/hooks/use-is-contract.ts +++ b/modules/web3/hooks/use-is-contract.ts @@ -1,11 +1,14 @@ import { useCallback } from 'react'; -import type { Address, Hex } from 'viem'; - import { useBytecode, usePublicClient } from 'wagmi'; +import { useDappStatus } from './use-dapp-status'; + +import type { Address, Hex } from 'viem'; + // helper hook until migration to wagmi is complete export const useGetIsContract = () => { - const client = usePublicClient(); + const { chainId } = useDappStatus(); + const client = usePublicClient({ chainId }); return useCallback( async (address: Address) => { const code = await client?.getCode({ address }); @@ -22,8 +25,10 @@ const toBool = (data: Hex | undefined) => { // helper hook until migration to wagmi is complete export const useIsContract = (account?: string | null) => { + const { chainId } = useDappStatus(); return useBytecode({ address: account as Address, + chainId, query: { enabled: !!account, select: toBool, diff --git a/modules/web3/hooks/use-is-multisig.ts b/modules/web3/hooks/use-is-multisig.ts new file mode 100644 index 000000000..a6699b44b --- /dev/null +++ b/modules/web3/hooks/use-is-multisig.ts @@ -0,0 +1,8 @@ +import { useIsContract } from './use-is-contract'; +import { useDappStatus } from './use-dapp-status'; + +export const useIsMultisig = () => { + const { address } = useDappStatus(); + const { data: isMultisig, isLoading } = useIsContract(address); + return { isMultisig, isLoading }; +}; diff --git a/shared/hooks/useMaxGasPrice.ts b/modules/web3/hooks/use-max-gas-price.ts similarity index 85% rename from shared/hooks/useMaxGasPrice.ts rename to modules/web3/hooks/use-max-gas-price.ts index dae7cd3b5..4f2a2853e 100644 --- a/shared/hooks/useMaxGasPrice.ts +++ b/modules/web3/hooks/use-max-gas-price.ts @@ -2,6 +2,8 @@ import { useFeeHistory } from 'wagmi'; import { BigNumber } from 'ethers'; import type { GetFeeHistoryReturnType } from 'viem'; +import { useDappStatus } from './use-dapp-status'; + const REWARD_PERCENTILES = [25]; const feeHistoryToMaxFee = ({ @@ -20,10 +22,13 @@ const feeHistoryToMaxFee = ({ return BigNumber.from(maxFeePerGas); }; -export const useMaxGasPrice = () => { +export const useMaxGasPrice = (chainId?: number) => { + const { chainId: dappChainId } = useDappStatus(); + const { data, isLoading, error, isFetching, refetch } = useFeeHistory({ blockCount: 5, blockTag: 'pending', + chainId: chainId || dappChainId, rewardPercentiles: REWARD_PERCENTILES, query: { select: feeHistoryToMaxFee, diff --git a/shared/hooks/use-stETH-by-wstETH-on-l2.ts b/modules/web3/hooks/use-stETH-by-wstETH-on-l2.ts similarity index 93% rename from shared/hooks/use-stETH-by-wstETH-on-l2.ts rename to modules/web3/hooks/use-stETH-by-wstETH-on-l2.ts index 2901fea58..532a92a05 100644 --- a/shared/hooks/use-stETH-by-wstETH-on-l2.ts +++ b/modules/web3/hooks/use-stETH-by-wstETH-on-l2.ts @@ -1,7 +1,7 @@ import { BigNumber } from 'ethers'; import useSWR from 'swr'; -import { useLidoSDK } from 'providers/lido-sdk'; +import { useLidoSDK } from 'modules/web3'; export const useStETHByWstETHOnL2 = (wsteth: BigNumber | undefined) => { const { l2 } = useLidoSDK(); diff --git a/shared/hooks/use-staking-limit-warning.ts b/modules/web3/hooks/use-staking-limit-warning.ts similarity index 90% rename from shared/hooks/use-staking-limit-warning.ts rename to modules/web3/hooks/use-staking-limit-warning.ts index 30696a904..3275c882c 100644 --- a/shared/hooks/use-staking-limit-warning.ts +++ b/modules/web3/hooks/use-staking-limit-warning.ts @@ -1,4 +1,4 @@ -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from './use-dapp-status'; import { LIMIT_LEVEL } from 'types'; export const useStakingLimitWarning = (stakingLimitLevel?: LIMIT_LEVEL) => { diff --git a/shared/hooks/use-tx-conformation.ts b/modules/web3/hooks/use-tx-conformation.ts similarity index 79% rename from shared/hooks/use-tx-conformation.ts rename to modules/web3/hooks/use-tx-conformation.ts index 7560046ef..096a7d786 100644 --- a/shared/hooks/use-tx-conformation.ts +++ b/modules/web3/hooks/use-tx-conformation.ts @@ -3,10 +3,13 @@ import type { Hash } from 'viem'; import { waitForTransactionReceipt } from 'viem/actions'; import { useClient } from 'wagmi'; +import { useDappStatus } from './use-dapp-status'; + // 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(); + const { chainId } = useDappStatus(); + const client = useClient({ chainId }); return useCallback( (hash: string) => { return waitForTransactionReceipt(client as any, { diff --git a/modules/web3/hooks/use-wagmi-key.ts b/modules/web3/hooks/use-wagmi-key.ts new file mode 100644 index 000000000..a05688778 --- /dev/null +++ b/modules/web3/hooks/use-wagmi-key.ts @@ -0,0 +1,8 @@ +import { useDappStatus } from './use-dapp-status'; + +// In order to simplify side effects of switching wallets/chains +// we can remount by this key, resetting all internal states +export const useWagmiKey = () => { + const { chainId, address } = useDappStatus(); + return `${address ?? 'NO_ADDRESS'}_${chainId}`; +}; diff --git a/shared/hooks/use-wstETH-by-stETH-on-l2.ts b/modules/web3/hooks/use-wstETH-by-stETH-on-l2.ts similarity index 93% rename from shared/hooks/use-wstETH-by-stETH-on-l2.ts rename to modules/web3/hooks/use-wstETH-by-stETH-on-l2.ts index 2f30ae1a0..e30fdf8df 100644 --- a/shared/hooks/use-wstETH-by-stETH-on-l2.ts +++ b/modules/web3/hooks/use-wstETH-by-stETH-on-l2.ts @@ -1,7 +1,7 @@ import { BigNumber } from 'ethers'; import useSWR from 'swr'; -import { useLidoSDK } from 'providers/lido-sdk'; +import { useLidoSDK } from 'modules/web3'; export const useWstETHByStETHOnL2 = (steth: BigNumber | undefined) => { const { l2 } = useLidoSDK(); diff --git a/modules/web3/index.ts b/modules/web3/index.ts new file mode 100644 index 000000000..5ed46339d --- /dev/null +++ b/modules/web3/index.ts @@ -0,0 +1,8 @@ +export * from './hooks'; +export * from './utils'; +export { + Web3Provider, + useLidoSDK, + SupportL2Chains, + DAPP_CHAIN_TYPE, +} from './web3-provider'; diff --git a/modules/web3/utils/index.ts b/modules/web3/utils/index.ts new file mode 100644 index 000000000..22c7d5525 --- /dev/null +++ b/modules/web3/utils/index.ts @@ -0,0 +1 @@ +export * from './send-tx/send-tx'; diff --git a/utils/apply-round-up-gas-limit.ts b/modules/web3/utils/send-tx/apply-round-up-gas-limit.ts similarity index 100% rename from utils/apply-round-up-gas-limit.ts rename to modules/web3/utils/send-tx/apply-round-up-gas-limit.ts diff --git a/utils/estimate-gas.ts b/modules/web3/utils/send-tx/estimate-gas.ts similarity index 80% rename from utils/estimate-gas.ts rename to modules/web3/utils/send-tx/estimate-gas.ts index 51f4dca20..e1bc50388 100644 --- a/utils/estimate-gas.ts +++ b/modules/web3/utils/send-tx/estimate-gas.ts @@ -1,5 +1,5 @@ -import { StaticJsonRpcBatchProvider } from '@lidofinance/eth-providers'; -import { PopulatedTransaction } from 'ethers'; +import type { StaticJsonRpcBatchProvider } from '@lidofinance/eth-providers'; +import type { PopulatedTransaction } from 'ethers'; export const estimateGas = async ( tx: PopulatedTransaction, diff --git a/utils/getFeeData.ts b/modules/web3/utils/send-tx/get-fee-data.ts similarity index 100% rename from utils/getFeeData.ts rename to modules/web3/utils/send-tx/get-fee-data.ts diff --git a/modules/web3/utils/send-tx/index.ts b/modules/web3/utils/send-tx/index.ts new file mode 100644 index 000000000..86341c314 --- /dev/null +++ b/modules/web3/utils/send-tx/index.ts @@ -0,0 +1 @@ +export { sendTx } from './send-tx'; diff --git a/utils/send-tx.ts b/modules/web3/utils/send-tx/send-tx.ts similarity index 91% rename from utils/send-tx.ts rename to modules/web3/utils/send-tx/send-tx.ts index 3b56366c6..211617548 100644 --- a/utils/send-tx.ts +++ b/modules/web3/utils/send-tx/send-tx.ts @@ -3,11 +3,11 @@ import type { Web3Provider, } from '@ethersproject/providers'; import type { PopulatedTransaction } from 'ethers'; +import { applyGasLimitRatio } from 'utils/apply-gas-limit-ratio'; -import { getFeeData } from './getFeeData'; +import { getFeeData } from './get-fee-data'; import { estimateGas } from './estimate-gas'; -import { applyGasLimitRatio } from 'utils/apply-gas-limit-ratio'; -import { applyRoundUpGasLimit } from 'utils/apply-round-up-gas-limit'; +import { applyRoundUpGasLimit } from './apply-round-up-gas-limit'; export type SendTxOptions = { tx: PopulatedTransaction; diff --git a/shared/wallet/connect-wallet-modal/connect-wallet-modal.tsx b/modules/web3/web3-provider/connect-wallet-modal/connect-wallet-modal.tsx similarity index 100% rename from shared/wallet/connect-wallet-modal/connect-wallet-modal.tsx rename to modules/web3/web3-provider/connect-wallet-modal/connect-wallet-modal.tsx diff --git a/shared/wallet/connect-wallet-modal/index.ts b/modules/web3/web3-provider/connect-wallet-modal/index.ts similarity index 100% rename from shared/wallet/connect-wallet-modal/index.ts rename to modules/web3/web3-provider/connect-wallet-modal/index.ts diff --git a/modules/web3/web3-provider/dapp-chain.tsx b/modules/web3/web3-provider/dapp-chain.tsx new file mode 100644 index 000000000..1fc844789 --- /dev/null +++ b/modules/web3/web3-provider/dapp-chain.tsx @@ -0,0 +1,197 @@ +import React, { + createContext, + useContext, + useState, + useMemo, + useEffect, +} from 'react'; +import invariant from 'tiny-invariant'; + +import { CHAINS, isSDKSupportedL2Chain } from 'consts/chains'; +import { useAccount } from 'wagmi'; +import { config } from 'config'; +import { useLidoSDK } from './lido-sdk'; +import { wagmiChainMap } from './web3-provider'; + +export enum DAPP_CHAIN_TYPE { + Ethereum = 'Ethereum', + Optimism = 'Optimism', +} + +type DappChainContextValue = { + chainType: DAPP_CHAIN_TYPE; + setChainType: React.Dispatch>; + supportedChainIds: number[]; + isChainTypeMatched: boolean; + isChainTypeOnL2: boolean; +}; + +export type SupportedChainLabels = { + [key in DAPP_CHAIN_TYPE]: string; +}; + +type UseDappChainValue = { + // Current DApp chain ID (may not match with chainType) + chainId: number; + // Chain ID by current chainType + chainTypeChainId: number; + + isSupportedChain: boolean; + supportedChainTypes: DAPP_CHAIN_TYPE[]; + supportedChainLabels: SupportedChainLabels; +} & DappChainContextValue; + +const DappChainContext = createContext(null); +DappChainContext.displayName = 'DappChainContext'; + +const ETHEREUM_CHAINS = new Set([ + CHAINS.Mainnet, + CHAINS.Holesky, + CHAINS.Sepolia, +]); + +const OPTIMISM_CHAINS = new Set([CHAINS.Optimism, CHAINS.OptimismSepolia]); + +const getChainTypeByChainId = (chainId?: number): DAPP_CHAIN_TYPE | null => { + if (!chainId) return null; + if (ETHEREUM_CHAINS.has(chainId)) { + return DAPP_CHAIN_TYPE.Ethereum; + } else if (OPTIMISM_CHAINS.has(chainId)) { + return DAPP_CHAIN_TYPE.Optimism; + } + return null; +}; + +// At the current stage of the widget we don't care what ID is returned: +// - 'chainTypeChainId' is only used for statistics; +// - on the prod environment, the 'function map' of 'chainType' to 'chainId' will be 1 to 1 (bijective mapping). +const getChainIdByChainType = ( + chainType: DAPP_CHAIN_TYPE, + supportedChainIds: number[], +): number | undefined => + supportedChainIds.find((id) => getChainTypeByChainId(id) === chainType); + +export const useDappChain = (): UseDappChainValue => { + const context = useContext(DappChainContext); + invariant(context, 'useDappChain was used outside of DappChainProvider'); + + const { chainId: dappChain } = useLidoSDK(); + const { chainId: walletChain } = useAccount(); + + return useMemo(() => { + const supportedChainTypes = context.supportedChainIds + .map(getChainTypeByChainId) + .filter( + (chainType, index, array) => + // duplicate/invalid pruning + stable order + chainType && array.indexOf(chainType) === index, + ) as DAPP_CHAIN_TYPE[]; + + const getChainLabelByType = (chainType: DAPP_CHAIN_TYPE) => { + // all testnets for chainType + const testnetsForType = context.supportedChainIds + .filter((id) => chainType == getChainTypeByChainId(id)) + .map((id) => wagmiChainMap[id]) + .filter((chain) => chain.testnet) + .map((chain) => chain.name); + + return ( + chainType + + (testnetsForType.length > 0 ? `(${testnetsForType.join(',')})` : '') + ); + }; + + const supportedChainLabels = supportedChainTypes.reduce( + (acc, chainType) => ({ + ...acc, + [chainType]: getChainLabelByType(chainType), + }), + {}, + ) as SupportedChainLabels; + + const chainTypeChainId = + getChainIdByChainType(context.chainType, context.supportedChainIds) ?? + config.defaultChain; + + return { + ...context, + chainId: context.supportedChainIds.includes(dappChain) + ? dappChain + : config.defaultChain, + chainTypeChainId, + isSupportedChain: walletChain + ? context.supportedChainIds.includes(walletChain) + : true, + supportedChainTypes, + supportedChainLabels, + }; + }, [context, dappChain, walletChain]); +}; + +export const SupportL2Chains: React.FC = ({ + children, +}) => { + const { chainId: walletChainId, isConnected } = useAccount(); + const [chainType, setChainType] = useState( + DAPP_CHAIN_TYPE.Ethereum, + ); + + useEffect(() => { + if (!walletChainId) { + // This code resets 'chainType' to ETH when the wallet is disconnected. + // It also works on the first rendering, but we don't care, because the 'chainType' by default is ETH. + // Don't use it if you need to do something strictly, only when the wallet is disconnected. + setChainType(DAPP_CHAIN_TYPE.Ethereum); + return; + } + + if (isConnected) { + const newChainType = getChainTypeByChainId(walletChainId); + if (newChainType) setChainType(newChainType); + } + }, [walletChainId, isConnected, setChainType]); + + return ( + ({ + chainType, + setChainType, + supportedChainIds: config.supportedChains, + isChainTypeMatched: + chainType === getChainTypeByChainId(walletChainId), + // At the moment a simple check is enough for us, + // however in the future we will either rethink this flag + // or use an array or Set (for example with L2_DAPP_CHAINS_TYPE) + isChainTypeOnL2: chainType === DAPP_CHAIN_TYPE.Optimism, + }), + [chainType, walletChainId], + )} + > + {children} + + ); +}; + +const onlyL1ChainsValue = { + chainType: DAPP_CHAIN_TYPE.Ethereum, + // only L1 chains + supportedChainIds: config.supportedChains.filter( + (chain) => !isSDKSupportedL2Chain(chain), + ), + isChainTypeMatched: true, + isChainTypeOnL2: false, + setChainType: () => {}, +}; + +// Value of this context only allows L1 chains and no chain switch +// this is actual for most pages and can be overriden by SupportL2Chains on per page basis +// for safety reasons this cannot be default context value +// in order to prevent accidental useDappChain/useDappStatus misusage in top-lvl components +export const SupportL1Chains: React.FC = ({ + children, +}) => ( + + {children} + +); diff --git a/modules/web3/web3-provider/index.ts b/modules/web3/web3-provider/index.ts new file mode 100644 index 000000000..ca2538c66 --- /dev/null +++ b/modules/web3/web3-provider/index.ts @@ -0,0 +1,3 @@ +export { Web3Provider } from './web3-provider'; +export { useLidoSDK } from './lido-sdk'; +export { SupportL2Chains, DAPP_CHAIN_TYPE } from './dapp-chain'; diff --git a/providers/lido-sdk.tsx b/modules/web3/web3-provider/lido-sdk.tsx similarity index 95% rename from providers/lido-sdk.tsx rename to modules/web3/web3-provider/lido-sdk.tsx index 45e25dc72..87b0795f7 100644 --- a/providers/lido-sdk.tsx +++ b/modules/web3/web3-provider/lido-sdk.tsx @@ -18,7 +18,7 @@ import { LidoSDKL2 } from '@lidofinance/lido-ethereum-sdk/l2'; import { LidoSDKWrap } from '@lidofinance/lido-ethereum-sdk/wrap'; import { config } from 'config'; -import { useTokenTransferSubscription } from 'shared/hooks/use-balance'; +import { useTokenTransferSubscription } from 'modules/web3/hooks/use-balance'; import { LIDO_L2_CONTRACT_ADDRESSES } from '@lidofinance/lido-ethereum-sdk/common'; type LidoSDKContextValue = { @@ -27,6 +27,7 @@ type LidoSDKContextValue = { wstETH: LidoSDKwstETH; l2: LidoSDKL2; wrap: LidoSDKWrap; + chainId: CHAINS; isL2: boolean; subscribeToTokenUpdates: ReturnType; }; @@ -46,7 +47,6 @@ export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { const chainId = useChainId(); const { data: walletClient } = useWalletClient({ chainId }); const publicClient = usePublicClient({ chainId }); - // reset internal wagmi state after disconnect const { isConnected } = useAccount(); @@ -86,6 +86,7 @@ export const LidoSDKProvider = ({ children }: React.PropsWithChildren) => { wstETH, wrap, l2, + chainId: core.chainId, isL2: !!LIDO_L2_CONTRACT_ADDRESSES[chainId as CHAINS], subscribeToTokenUpdates: subscribe, }; diff --git a/modules/web3/web3-provider/sdk-legacy.tsx b/modules/web3/web3-provider/sdk-legacy.tsx new file mode 100644 index 000000000..8382bcc85 --- /dev/null +++ b/modules/web3/web3-provider/sdk-legacy.tsx @@ -0,0 +1,84 @@ +import React, { PropsWithChildren, useMemo } from 'react'; +import { useAccount, usePublicClient } from 'wagmi'; + +import { type Network, Web3Provider } from '@ethersproject/providers'; +import { ProviderSDK } from '@lido-sdk/react'; + +import { useLidoSDK } from './lido-sdk'; +import { config } from 'config'; +import { isSDKSupportedL2Chain } from 'consts/chains'; + +// Stabilizes network detection to prevent repeated chainId calls +class EthersToViemProvider extends Web3Provider { + detectNetwork(): Promise { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + if (!this._cache['detectNetwork']) { + this._cache['detectNetwork'] = this._uncachedDetectNetwork(); + } + return this._cache['detectNetwork']; + } +} + +export const SDKLegacyProvider = ({ children }: PropsWithChildren) => { + const { defaultChain, supportedChains, PROVIDER_POLLING_INTERVAL } = config; + const { address } = useAccount(); + const { core, isL2, chainId } = useLidoSDK(); + + const supportedChainIds = useMemo( + () => supportedChains.filter((chain) => !isSDKSupportedL2Chain(chain)), + [supportedChains], + ); + + const ethersWeb3Provider = useMemo(() => { + if (isL2 || !core.web3Provider) return undefined; + const { chain, web3Provider } = core; + const transport = web3Provider.transport; + + const provider = new Web3Provider(transport, { + chainId: chain.id, + name: chain.name, + ensAddress: chain.contracts?.ensRegistry?.address, + }); + provider.pollingInterval = PROVIDER_POLLING_INTERVAL; + return provider; + }, [isL2, core, PROVIDER_POLLING_INTERVAL]); + + const onlyL1chainId = useMemo(() => { + if (ethersWeb3Provider) { + return chainId; + } + return defaultChain; + }, [chainId, defaultChain, ethersWeb3Provider]); + + const onlyL1publicClient = usePublicClient({ chainId: onlyL1chainId }); + const publicMainnetClient = usePublicClient({ chainId: 1 }); + + // only Web3Provider can accept viem transport + const providerRpc = useMemo(() => { + return ( + onlyL1publicClient && + new EthersToViemProvider(onlyL1publicClient.transport, onlyL1chainId) + ); + }, [onlyL1chainId, onlyL1publicClient]); + + const providerMainnetRpc = useMemo(() => { + return ( + publicMainnetClient && + new EthersToViemProvider(publicMainnetClient.transport, 1) + ); + }, [publicMainnetClient]); + + return ( + // @ts-expect-error Property children does not exist on type + + {children} + + ); +}; diff --git a/utils/use-web3-transport.ts b/modules/web3/web3-provider/use-web3-transport.ts similarity index 66% rename from utils/use-web3-transport.ts rename to modules/web3/web3-provider/use-web3-transport.ts index 78c906cda..e68bac77d 100644 --- a/utils/use-web3-transport.ts +++ b/modules/web3/web3-provider/use-web3-transport.ts @@ -11,28 +11,41 @@ import { UnsupportedProviderMethodError, InvalidParamsRpcError, } from 'viem'; +import { mainnet } from 'viem/chains'; + 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 +// We disable those methods so wagmi uses getLogs instead to watch events +// Filters are not suitable for public rpc and break when changing between fallbacks const DISABLED_METHODS = new Set([ 'eth_newFilter', 'eth_getFilterChanges', 'eth_uninstallFilter', ]); -const NOOP = () => {}; - // Viem transport wrapper that allows runtime changes via setter const runtimeMutableTransport = ( mainTransports: Transport[], ): [Transport, (t: Transport | null) => void] => { let withInjectedTransport: Transport | null = null; + + // tuple [RuntimeMutableTransport(), injectedTransporterSetter()] return [ (params) => { const defaultTransport = fallback(mainTransports)(params); - let responseFn: OnResponseFn = NOOP; + let externalOnResponse: OnResponseFn; + + const onResponse: OnResponseFn = (params) => { + if (params.status === 'error' && !(params as any).skipLog) { + console.warn( + `[runtimeMutableTransport] error in RuntimeMutableTransport(using injected: ${!!withInjectedTransport})`, + params, + ); + } + externalOnResponse?.(params); + }; + return createTransport( { key: 'RuntimeMutableTransport', @@ -47,13 +60,15 @@ const runtimeMutableTransport = ( const error = new UnsupportedProviderMethodError( new Error(`Method ${requestParams.method} is not supported`), ); - responseFn({ + onResponse({ error, method: requestParams.method, params: params as unknown[], transport, status: 'error', - }); + // skip logging because we expect wagmi to try those + skipLog: true, + } as any); throw error; } @@ -63,14 +78,10 @@ const runtimeMutableTransport = ( // works for empty array, empty string and all falsish values !requestParams.params[0]?.address?.length ) { - console.warn( - '[runtimeMutableTransport] Invalid empty getLogs', - requestParams, - ); const error = new InvalidParamsRpcError( new Error(`Empty address for eth_getLogs is not supported`), ); - responseFn({ + onResponse({ error, method: requestParams.method, params: params as unknown[], @@ -80,14 +91,19 @@ const runtimeMutableTransport = ( throw error; } - transport.value?.onResponse(responseFn); + transport.value?.onResponse(onResponse); return transport.request(requestParams, options); }, + // crucial cause we quack like a fallback transport and some connectors(WC) rely on this type: 'fallback', }, + // transport.value contents { + // this is fallbackTransport specific field, used by WC connectors to extract rpc Urls + // we can use defaultTransport because no injected transport transports: defaultTransport.value?.transports, - onResponse: (fn: OnResponseFn) => (responseFn = fn), + // providers that use this transport, use this to set onResponse callback for transport, + onResponse: (fn: OnResponseFn) => (externalOnResponse = fn), }, ); }, @@ -104,27 +120,37 @@ const runtimeMutableTransport = ( ]; }; -// returns Viem transport map that uses browser wallet RPC provider when avaliable fallbacked by our RPC +// returns Viem transport map that uses browser wallet RPC provider when available fallbacked by our RPC and default RPCs export const useWeb3Transport = ( supportedChains: Chain[], backendRpcMap: Record, ) => { + const supportedChainsWithMainnet = useMemo( + () => + // create a copy of the array in any way + supportedChains.includes(mainnet) + ? [...supportedChains] + : [...supportedChains, mainnet], + [supportedChains], + ); + const { transportMap, setTransportMap } = useMemo(() => { - return supportedChains.reduce( + const batchConfig = { + wait: config.PROVIDER_BATCH_TIME, + batchSize: config.PROVIDER_MAX_BATCH, + }; + + return supportedChainsWithMainnet.reduce( ({ transportMap, setTransportMap }, chain) => { const [transport, setTransport] = runtimeMutableTransport([ + // api/rpc http(backendRpcMap[chain.id], { - batch: { - wait: config.PROVIDER_BATCH_TIME, - batchSize: config.PROVIDER_MAX_BATCH, - }, + batch: batchConfig, name: backendRpcMap[chain.id], }), + // fallback rpc from wagmi.chains like cloudfare-eth http(undefined, { - batch: { - wait: config.PROVIDER_BATCH_TIME, - batchSize: config.PROVIDER_MAX_BATCH, - }, + batch: batchConfig, name: 'default HTTP RPC', }), ]); @@ -144,11 +170,11 @@ export const useWeb3Transport = ( setTransportMap: {} as Record void>, }, ); - }, [supportedChains, backendRpcMap]); + }, [supportedChainsWithMainnet, backendRpcMap]); const onActiveConnection = useCallback( async (activeConnection: Connection | null) => { - for (const chain of supportedChains) { + for (const chain of supportedChainsWithMainnet) { const setTransport = setTransportMap[chain.id]; if ( activeConnection && @@ -163,7 +189,7 @@ export const useWeb3Transport = ( } else setTransport(null); } }, - [setTransportMap, supportedChains], + [setTransportMap, supportedChainsWithMainnet], ); return { transportMap, onActiveConnection }; diff --git a/providers/web3.tsx b/modules/web3/web3-provider/web3-provider.tsx similarity index 85% rename from providers/web3.tsx rename to modules/web3/web3-provider/web3-provider.tsx index 43e787c80..7beda21a6 100644 --- a/providers/web3.tsx +++ b/modules/web3/web3-provider/web3-provider.tsx @@ -13,14 +13,16 @@ import { config } from 'config'; import { useUserConfig } from 'config/user-config'; import { useGetRpcUrlByChainId } from 'config/rpc'; import { CHAINS } from 'consts/chains'; -import { ConnectWalletModal } from 'shared/wallet/connect-wallet-modal'; +import { ConnectWalletModal } from './connect-wallet-modal'; +import { useWeb3Transport } from './use-web3-transport'; +import { LidoSDKProvider } from './lido-sdk'; import { SDKLegacyProvider } from './sdk-legacy'; -import { useWeb3Transport } from 'utils/use-web3-transport'; +import { SupportL1Chains } from './dapp-chain'; type ChainsList = [wagmiChains.Chain, ...wagmiChains.Chain[]]; -const wagmiChainMap = Object.values(wagmiChains).reduce( +export const wagmiChainMap = Object.values(wagmiChains).reduce( (acc, chain) => { acc[chain.id] = chain; return acc; @@ -36,7 +38,7 @@ const queryClient = new QueryClient({ }, }); -const Web3Provider: FC = ({ children }) => { +export const Web3Provider: FC = ({ children }) => { const { defaultChain: defaultChainId, supportedChainIds, @@ -115,17 +117,14 @@ const Web3Provider: FC = ({ children }) => { walletDataList={walletsDataList} > {isWalletConnectionAllowed && } - - {children} - - + + + {children} + + + ); }; - -export default Web3Provider; diff --git a/package.json b/package.json index ddbd229b5..3943505a8 100644 --- a/package.json +++ b/package.json @@ -38,7 +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.5.0-alpha.3", + "@lidofinance/lido-ethereum-sdk": "4.0.0", "@lidofinance/lido-ui": "^3.26.0", "@lidofinance/next-api-wrapper": "^0.45.1", "@lidofinance/next-ip-rate-limit": "^0.45.1", @@ -52,7 +52,6 @@ "date-fns": "2.29.2", "ethers": "^5.7.2", "fs-extra": "^10.1.0", - "gray-matter": "^4.0.3", "js-cookie": "^3.0.1", "lodash": "^4.17.21", "memory-cache": "^0.2.0", @@ -70,17 +69,14 @@ "react-hook-form": "^7.45.2", "react-is": "^18.2.0", "react-transition-group": "^4.4.2", - "reef-knot": "5.7.5", - "remark": "^13.0.0", - "remark-external-links": "^8.0.0", - "remark-html": "^13.0.1", + "reef-knot": "5.7.4", "styled-components": "^5.3.5", "swr": "^1.3.0", "tiny-async-pool": "^1.2.0", "tiny-invariant": "^1.1.0", "uuid": "^8.3.2", - "viem": "2.18.8", - "wagmi": "2.12.2" + "viem": "2.21.25", + "wagmi": "2.12.17" }, "devDependencies": { "@commitlint/cli": "^17.4.4", diff --git a/pages/rewards.tsx b/pages/rewards.tsx index 5e1184e33..0d7fea95f 100644 --- a/pages/rewards.tsx +++ b/pages/rewards.tsx @@ -23,6 +23,7 @@ const Rewards: FC = () => { reward tracker. View stETH balances, historical rewards and transfers." /> + diff --git a/pages/withdrawals/[mode].tsx b/pages/withdrawals/[mode].tsx index b4bf20e70..2065ecf15 100644 --- a/pages/withdrawals/[mode].tsx +++ b/pages/withdrawals/[mode].tsx @@ -5,7 +5,7 @@ import Head from 'next/head'; import { WithdrawalsTabs } from 'features/withdrawals'; import { WithdrawalsProvider } from 'features/withdrawals/contexts/withdrawals-context'; import { Layout } from 'shared/components'; -import { useWagmiKey } from 'shared/hooks/use-wagmi-key'; +import { useWagmiKey } from 'modules/web3'; import { getDefaultStaticProps } from 'utilsApi/get-default-static-props'; const Withdrawals: FC = ({ mode }) => { diff --git a/pages/wrap/[[...mode]].tsx b/pages/wrap/[[...mode]].tsx index 2aab2a9b3..d33aff243 100644 --- a/pages/wrap/[[...mode]].tsx +++ b/pages/wrap/[[...mode]].tsx @@ -4,22 +4,24 @@ import Head from 'next/head'; import { WrapUnwrapTabs } from 'features/wsteth/wrap-unwrap-tabs'; import { Layout } from 'shared/components'; -import { useWagmiKey } from 'shared/hooks/use-wagmi-key'; +import { SupportL2Chains, useWagmiKey } from 'modules/web3'; import { getDefaultStaticProps } from 'utilsApi/get-default-static-props'; const WrapPage: FC = ({ mode }) => { const key = useWagmiKey(); return ( - - - Wrap | Lido - - - + + + + Wrap | Lido + + + + ); }; diff --git a/providers/dapp-chain.tsx b/providers/dapp-chain.tsx deleted file mode 100644 index 1a7dc3a5e..000000000 --- a/providers/dapp-chain.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { createContext, useContext, useState, useCallback } from 'react'; -import invariant from 'tiny-invariant'; - -import { CHAINS } from 'consts/chains'; - -export const ETHEREUM = 'ETHEREUM'; -export const OPTIMISM = 'OPTIMISM'; -export const UNKNOWN = 'UNKNOWN'; - -export type ChainNameType = 'ETHEREUM' | 'OPTIMISM'; -type ChainNameOrUnknownType = ChainNameType | 'UNKNOWN'; - -const getChainMainnetNameByChainId = ( - chainId: number, -): ChainNameOrUnknownType => { - if ([CHAINS.Mainnet, CHAINS.Holesky, CHAINS.Sepolia].includes(chainId)) { - return ETHEREUM; - } else if ([CHAINS.Optimism, CHAINS.OptimismSepolia].includes(chainId)) { - return OPTIMISM; - } else { - return UNKNOWN; - } -}; - -interface ContextValue { - chainName: string; - setChainName: React.Dispatch>; - getChainMainnetNameByChainId: (chainId: number) => ChainNameOrUnknownType; - isMatchDappChainAndWalletChain: ( - walletChainId?: number | undefined, - ) => boolean; -} - -const DappChainContext = createContext(undefined); - -export const useDappChain = () => { - const context = useContext(DappChainContext); - invariant(context, 'useDappChain was used outside of DappChainProvider'); - return context; -}; - -export const DappChainProvider: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => { - const [chainName, setChainName] = useState(ETHEREUM); - - const isMatchDappChainAndWalletChain = useCallback( - (walletChainId?: number | undefined): boolean => { - if (!walletChainId) return false; - return chainName === getChainMainnetNameByChainId(walletChainId); - }, - [chainName], - ); - - return ( - - {children} - - ); -}; diff --git a/providers/index.tsx b/providers/index.tsx index 5a75f2fc9..5e77f93ef 100644 --- a/providers/index.tsx +++ b/providers/index.tsx @@ -4,13 +4,12 @@ import { CookieThemeProvider } from '@lidofinance/lido-ui'; import { GlobalStyle } from 'styles'; import { ConfigProvider } from 'config'; -import { DappChainProvider } from './dapp-chain'; +import { Web3Provider } from 'modules/web3'; + import { AppFlagProvider } from './app-flag'; 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'; type ProvidersProps = { prefetchedManifest?: unknown; @@ -22,20 +21,16 @@ export const Providers: FC> = ({ }) => ( - - - - - - - - {children} - - - - - - + + + + + + {children} + + + + ); diff --git a/providers/rewardsHistory.tsx b/providers/rewardsHistory.tsx index 5230c38d7..7b6ec9677 100644 --- a/providers/rewardsHistory.tsx +++ b/providers/rewardsHistory.tsx @@ -19,7 +19,7 @@ import { useGetCurrentAddress, } from 'features/rewards/hooks'; import { getCurrencyCookie } from 'features/rewards/components/CurrencySelector'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; export type RewardsHistoryValue = { currencyObject: CurrencyType; @@ -49,8 +49,7 @@ export const RewardsHistoryContext = createContext({} as RewardsHistoryValue); const RewardsHistoryProvider: FC = (props) => { const { children } = props; - const { isWalletConnected, isSupportedChain, isAccountActiveOnL2 } = - useDappStatus(); + const { isSupportedChain } = useDappStatus(); const [currency, setCurrency] = useState(DEFAULT_CURRENCY.id); @@ -91,11 +90,7 @@ const RewardsHistoryProvider: FC = (props) => { const currencyObject = getCurrency(currency); - const isDataAvailable = useMemo(() => { - const isDataNotAvailable = - !data || (isWalletConnected && !isSupportedChain) || isAccountActiveOnL2; - return !isDataNotAvailable; - }, [data, isWalletConnected, isSupportedChain, isAccountActiveOnL2]); + const isDataAvailable = data && isSupportedChain; const value = useMemo( (): RewardsHistoryValue => ({ diff --git a/providers/sdk-legacy.tsx b/providers/sdk-legacy.tsx deleted file mode 100644 index 64db1ef99..000000000 --- a/providers/sdk-legacy.tsx +++ /dev/null @@ -1,126 +0,0 @@ -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'; - -import { Web3Provider } from '@ethersproject/providers'; -import { ProviderSDK } from '@lido-sdk/react'; -import { getStaticRpcBatchProvider } from '@lido-sdk/providers'; - -import { SDK_LEGACY_SUPPORTED_CHAINS } from 'consts/chains'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; - -type SDKLegacyProviderProps = PropsWithChildren<{ - defaultChainId: number; - pollingInterval: number; -}>; - -export const SDKLegacyProvider = ({ - children, - defaultChainId, - pollingInterval, -}: SDKLegacyProviderProps) => { - const { chainId: wagmiChainId = defaultChainId, address } = useAccount(); - const { supportedChains } = useSupportedChains(); - const { isDappActiveOnL1 } = useDappStatus(); - const config = useConfig(); - const client = useClient(); - const { rpc } = useReefKnotContext(); - - const [providerWeb3, setProviderWeb3] = useState(); - - useEffect(() => { - let isHookMounted = true; - - const getProviderTransport = async () => { - const { state } = config; - if (!state.current) return client?.transport; - const connector = state.connections.get(state.current)?.connector; - if (!connector) return client?.transport; - const provider: any = await connector.getProvider(); - return provider || client?.transport; - }; - - const getProviderValue = async () => { - // old sdk can only supports wallet connection on L1 - if (!client || !address || !isDappActiveOnL1) return undefined; - const { chain } = client; - const providerTransport = await getProviderTransport(); - - // https://wagmi.sh/core/guides/ethers#reference-implementation-1 - const provider = new Web3Provider(providerTransport, { - chainId: chain.id, - name: chain.name, - ensAddress: chain.contracts?.ensRegistry?.address, - }); - provider.pollingInterval = pollingInterval; - - return provider; - }; - - const getProviderAndSet = async () => { - const provider = await getProviderValue(); - if (isHookMounted) setProviderWeb3(provider); - }; - - void getProviderAndSet(); - - return () => { - isHookMounted = false; - }; - }, [ - config, - config.state, - client, - address, - isDappActiveOnL1, - pollingInterval, - ]); - - const supportedChainIds = useMemo( - () => supportedChains.map((chain) => chain.chainId), - [supportedChains], - ); - - const chainId = useMemo(() => { - if (providerWeb3) { - return supportedChainIds.indexOf(wagmiChainId) > -1 && - SDK_LEGACY_SUPPORTED_CHAINS.indexOf(wagmiChainId) > -1 - ? wagmiChainId - : defaultChainId; - } - return defaultChainId; - }, [defaultChainId, providerWeb3, supportedChainIds, wagmiChainId]); - - const providerRpc = useMemo( - () => getStaticRpcBatchProvider(chainId, rpc[chainId], 0, pollingInterval), - [rpc, chainId, pollingInterval], - ); - - const providerMainnetRpc = useMemo( - () => - getStaticRpcBatchProvider( - mainnet.id, - rpc[mainnet.id], - 0, - pollingInterval, - ), - [rpc, pollingInterval], - ); - - return ( - // @ts-expect-error Property children does not exist on type - - {children} - - ); -}; diff --git a/shared/components/data-table-row-steth-by-wsteth/data-table-row-steth-by-wsteth.tsx b/shared/components/data-table-row-steth-by-wsteth/data-table-row-steth-by-wsteth.tsx index cfaf78357..165e1460a 100644 --- a/shared/components/data-table-row-steth-by-wsteth/data-table-row-steth-by-wsteth.tsx +++ b/shared/components/data-table-row-steth-by-wsteth/data-table-row-steth-by-wsteth.tsx @@ -4,8 +4,8 @@ import { parseEther } from '@ethersproject/units'; import { DATA_UNAVAILABLE } from 'consts/text'; import { FormatToken } from 'shared/formatters'; import { useStethByWsteth } from 'shared/hooks'; -import { useStETHByWstETHOnL2 } from 'shared/hooks/use-stETH-by-wstETH-on-l2'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useStETHByWstETHOnL2 } from 'modules/web3'; +import { useDappStatus } from 'modules/web3'; const OneWsteth = parseEther('1'); @@ -16,11 +16,7 @@ type DataTableRowStethByWstethProps = { export const DataTableRowStethByWsteth = ({ toSymbol = 'stETH', }: DataTableRowStethByWstethProps) => { - const { - isWalletConnected, - isDappActiveOnL2, - isDappActiveAndNetworksMatched, - } = useDappStatus(); + const { isDappActiveOnL2 } = useDappStatus(); const stethByWsteth = useStethByWsteth( !isDappActiveOnL2 ? OneWsteth : undefined, ); @@ -38,9 +34,7 @@ export const DataTableRowStethByWsteth = ({ title="Exchange rate" loading={initialLoading} > - {isWalletConnected && !isDappActiveAndNetworksMatched ? ( - '-' - ) : data ? ( + {data ? ( <> 1 wstETH = li, - ol > li { - margin-top: 0; - margin-bottom: 0; - - & > p { - margin-top: 0; - margin-bottom: 0; - } - } - - a { - text-decoration: none; - } -`; - -export const Faq: FC = memo(({ faqList, replacements }) => { - return ( -
- {faqList.map(({ id, title, content }, index) => { - const html = replaceAll(content, replacements); - - return ( - - - - ); - })} -
- ); -}); diff --git a/shared/components/index.ts b/shared/components/index.ts index a783aa8cb..a1e4a2a8d 100644 --- a/shared/components/index.ts +++ b/shared/components/index.ts @@ -1,5 +1,4 @@ export { Layout } from './layout/layout'; -export { Faq } from './faq/faq'; export { BackgroundGradient } from './background-gradient/background-gradient'; export { Section } from './section/section'; export { TokenToWallet } from './token-to-wallet/token-to-wallet'; diff --git a/shared/components/layout/header/components/chain-switcher/chain-switcher.tsx b/shared/components/layout/header/components/chain-switcher/chain-switcher.tsx index c73dd4f3b..459fd5643 100644 --- a/shared/components/layout/header/components/chain-switcher/chain-switcher.tsx +++ b/shared/components/layout/header/components/chain-switcher/chain-switcher.tsx @@ -1,102 +1,64 @@ -import { - FC, - ReactNode, - useState, - useCallback, - useEffect, - useMemo, -} from 'react'; -import { useRouter } from 'next/router'; -import { useAccount } from 'wagmi'; +import { FC, useState, useRef, ReactNode } from 'react'; +import { DAPP_CHAIN_TYPE } from 'modules/web3'; +import { useDappStatus } from 'modules/web3'; -import { Option } from '@lidofinance/lido-ui'; +import { useClickOutside } from './hooks/use-click-outside'; +import { ChainSwitcherOptions } from './components/chain-switcher-options/chain-switcher-options'; +import { SelectIconTooltip } from './components/select-icon-tooltip/select-icon-tooltip'; +import { + ChainSwitcherWrapperStyled, + ChainSwitcherStyled, + IconStyle, + ArrowStyle, +} from './styles'; import { ReactComponent as OptimismLogo } from 'assets/icons/chain-toggler/optimism.svg'; import { ReactComponent as EthereumMainnetLogo } from 'assets/icons/chain-toggler/mainnet.svg'; -import { CHAINS } from 'consts/chains'; -import { - ChainNameType, - ETHEREUM, - OPTIMISM, - useDappChain, -} from 'providers/dapp-chain'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; - -import { SelectIconTooltip } from './components/select-icon-tooltip/select-icon-tooltip'; -import { SelectIconStyled, SelectIconWrapper } from './styles'; - -const iconsMap: Record = { - [ETHEREUM]: , - [OPTIMISM]: , +const iconsMap: Record = { + [DAPP_CHAIN_TYPE.Ethereum]: , + [DAPP_CHAIN_TYPE.Optimism]: , }; export const ChainSwitcher: FC = () => { - const { chainId } = useAccount(); - const { setChainName } = useDappChain(); - const { isDappActiveAndNetworksMatched } = useDappStatus(); - const router = useRouter(); + const { isDappActive, chainType, supportedChainTypes, setChainType } = + useDappStatus(); + const [opened, setOpened] = useState(false); + const selectRef = useRef(null); - const [value, setValue] = useState(ETHEREUM); + const isChainTypeUnlocked = supportedChainTypes.length > 1; - const isOnWrapUnwrapPage = useMemo( - () => router.pathname === '/wrap/[[...mode]]', - [router.pathname], - ); - - useEffect(() => { - if (!chainId) return; - - if ([CHAINS.Mainnet, CHAINS.Holesky, CHAINS.Sepolia].includes(chainId)) { - setValue(ETHEREUM); - setChainName(ETHEREUM); - } else if ( - [CHAINS.Optimism, CHAINS.OptimismSepolia].includes(chainId) && - isOnWrapUnwrapPage - ) { - setValue(OPTIMISM); - setChainName(OPTIMISM); - } - }, [chainId, isOnWrapUnwrapPage, setChainName]); - - useEffect(() => { - if (!isOnWrapUnwrapPage) { - setValue(ETHEREUM); - setChainName(ETHEREUM); - } - }, [isOnWrapUnwrapPage, setChainName]); - - const onChange = useCallback( - (value: any) => { - setValue(value as ChainNameType); - setChainName(value as ChainNameType); - }, - [setChainName], - ); + useClickOutside(selectRef, () => setOpened(false)); return ( - - + setOpened((prev) => !prev)} > - - - - {isOnWrapUnwrapPage && !isDappActiveAndNetworksMatched && ( - - {isOnWrapUnwrapPage - ? 'This network doesn’t match your wallet’s network' - : 'Don’t forget to switch to Ethereum'} - + {iconsMap[chainType]} + {isChainTypeUnlocked && } + + + {isChainTypeUnlocked && ( + <> + { + setChainType(chainType); + setOpened(false); + }} + opened={opened} + options={iconsMap} + /> + {!isDappActive && ( + + This network doesn’t match your wallet’s network + + )} + )} - + ); }; diff --git a/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/chain-switcher-options.tsx b/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/chain-switcher-options.tsx new file mode 100644 index 000000000..b2a546bfc --- /dev/null +++ b/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/chain-switcher-options.tsx @@ -0,0 +1,35 @@ +import { FC, ReactNode } from 'react'; +import { DAPP_CHAIN_TYPE } from 'modules/web3'; + +import { PopoverWrapperStyled, PopupStyled, OptionStyled } from './styles'; + +interface ChainSwitcherOptionsProps { + currentChainType: DAPP_CHAIN_TYPE; + onSelect: (chainType: DAPP_CHAIN_TYPE) => void; + opened: boolean; + options: Record; +} + +export const ChainSwitcherOptions: FC = ({ + currentChainType, + onSelect, + opened, + options, +}) => ( + // We need the 'PopoverWrapperStyled' for block any events as if you had set 'pointer-events: none' on the body + // while the 'PopupStyled' is opened + <> + + + {Object.entries(options).map(([chainType, icon]) => ( + onSelect(chainType as DAPP_CHAIN_TYPE)} + $active={chainType === currentChainType} + > + {icon} {chainType} + + ))} + + +); diff --git a/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/styles.tsx b/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/styles.tsx new file mode 100644 index 000000000..4246cc81a --- /dev/null +++ b/shared/components/layout/header/components/chain-switcher/components/chain-switcher-options/styles.tsx @@ -0,0 +1,127 @@ +import styled, { css } from 'styled-components'; + +export const PopoverWrapperStyled = styled.div<{ $backdrop: boolean }>` + z-index: 200; + position: fixed; + top: 0; + left: 0; + margin: 0; + padding: 0; + width: 100%; + height: ${({ $backdrop }) => ($backdrop ? '100%' : '0px')}; +`; + +type PopupMenuProps = { + $opened: boolean; +}; + +const visibleCSS = css` + opacity: 1; + + &[data-placement] { + transform: translate(0, 0); + } +`; + +const hiddenCSS = css` + opacity: 0; + + &[data-placement^='top'] { + transform: translateY(6px); + } + &[data-placement^='right'] { + transform: translateX(-6px); + } + &[data-placement^='bottom'] { + transform: translateY(-6px); + } + &[data-placement^='left'] { + transform: translateX(6px); + } +`; + +export const PopupStyled = styled.div` + z-index: 201; + min-width: 115px; + + position: absolute; + top: 48px; + + overflow: auto; + overflow-x: hidden; + box-sizing: border-box; + + margin: 0; + padding: 0; + + background: var(--lido-color-foreground); + font-size: ${({ theme }) => theme.fontSizesMap.xs}px; + line-height: 1.5em; + font-weight: 400; + + border-radius: ${({ theme }) => theme.borderRadiusesMap.lg}px; + box-shadow: ${({ theme }) => theme.boxShadows.xs} + var(--lido-color-shadowLight); + + transition: opacity 150ms ease; + transition-property: opacity, transform; + + ${({ $opened }) => ($opened ? visibleCSS : hiddenCSS)}; +`; + +type PopupMenuOptionProps = { + $active: boolean; +}; + +export const OptionStyled = styled.div` + display: flex; + align-items: center; + + width: 100%; + height: 44px; + + padding: 0 8px; + margin: 0; + box-sizing: border-box; + + text-align: left; + color: var(--lido-color-text); + transition: opacity 100ms; + cursor: pointer; + + // Fix the highlight by click + -webkit-tap-highlight-color: transparent; + outline: none; + + // Backgrounds + background: var(--lido-color-controlBg); + + ${({ theme, $active }) => + theme.name === 'dark' + ? css` + background: ${$active && '#34343D'}; + ` + : css` + background: ${$active && '#000A3D08'}; + `} + + &:not(:disabled):hover { + ${({ theme }) => + theme.name === 'dark' + ? css` + background: #34343d; + ` + : css` + background: #000a3d08; + `} + } + + // Option.Text + & > span { + margin-left: 8px; + + font-weight: 400; + font-size: 12px; + color: #7a8aa0; + } +`; diff --git a/shared/components/layout/header/components/chain-switcher/components/select-icon-tooltip/styles.tsx b/shared/components/layout/header/components/chain-switcher/components/select-icon-tooltip/styles.tsx index 13bd22244..d08a9a655 100644 --- a/shared/components/layout/header/components/chain-switcher/components/select-icon-tooltip/styles.tsx +++ b/shared/components/layout/header/components/chain-switcher/components/select-icon-tooltip/styles.tsx @@ -11,6 +11,7 @@ export const SelectIconTooltipWrapper = styled.div` left: 0; top: calc(100% + 16px); width: 244px; + z-index: 100; @media ${devicesHeaderMedia.mobile} { position: fixed; @@ -19,7 +20,6 @@ export const SelectIconTooltipWrapper = styled.div` right: 20px; top: unset; width: auto; - z-index: 5000; } `; diff --git a/shared/components/layout/header/components/chain-switcher/hooks/use-click-outside.ts b/shared/components/layout/header/components/chain-switcher/hooks/use-click-outside.ts new file mode 100644 index 000000000..75a4b1422 --- /dev/null +++ b/shared/components/layout/header/components/chain-switcher/hooks/use-click-outside.ts @@ -0,0 +1,26 @@ +import { useEffect } from 'react'; + +export const useClickOutside = ( + ref: React.RefObject, + onClickOutside: () => void, +) => { + useEffect(() => { + const handleClickOutside = (event: MouseEvent | TouchEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + onClickOutside(); + } + }; + + // Preventing double execution on mobile devices + if ('ontouchstart' in window) { + document.addEventListener('touchstart', handleClickOutside); + } else { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('touchstart', handleClickOutside); + }; + }, [ref, onClickOutside]); +}; diff --git a/shared/components/layout/header/components/chain-switcher/styles.tsx b/shared/components/layout/header/components/chain-switcher/styles.tsx index 1e37b0fdf..f2e15c1a8 100644 --- a/shared/components/layout/header/components/chain-switcher/styles.tsx +++ b/shared/components/layout/header/components/chain-switcher/styles.tsx @@ -1,36 +1,68 @@ import styled, { css } from 'styled-components'; -import { SelectIcon } from '@lidofinance/lido-ui'; -export const SelectIconWrapper = styled.div` +export const ChainSwitcherWrapperStyled = styled.div` position: relative; `; -export const SelectIconStyled = styled(SelectIcon)` - overflow: ${({ disabled }) => (disabled ? 'hidden' : 'visible')}; - width: ${({ disabled }) => (disabled ? '44px' : '68px')}; +export const ChainSwitcherStyled = styled.div<{ $disabled: boolean }>` + z-index: 202; + + display: inline-flex; + flex-grow: 1; + align-items: center; + justify-content: space-between; + + position: relative; + overflow: ${({ $disabled }) => ($disabled ? 'hidden' : 'visible')}; + box-sizing: border-box; + + width: ${({ $disabled }) => ($disabled ? '44px' : '68px')}; height: 44px; + margin-right: 12px; + padding: 9px 8px; + + font-weight: 400; + font-size: 14px; + color: var(--lido-color-text); + + border-radius: 10px; + transition: border-color 100ms; + cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')}; - cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')}; + // Fix the highlight by click + -webkit-tap-highlight-color: transparent; + outline: none; + + background: var(--lido-color-controlBg); &:not(:disabled):hover { - & > span { - ${({ theme, disabled }) => - theme.name === 'dark' - ? css` - background: ${!disabled && '#34343D'}; - ` - : css` - background: ${!disabled && '#000A3D08'}; - `} - } + ${({ theme, $disabled }) => + theme.name === 'dark' + ? css` + background: ${!$disabled && '#34343D'}; + ` + : css` + background: ${!$disabled && '#000A3D08'}; + `} } +`; - & > span { - border: 0; - padding-left: 8px; - padding-right: 14px; - } +export const IconStyle = styled.span` + display: flex; + align-items: center; + justify-content: center; + align-self: stretch; + justify-self: stretch; +`; - border-radius: 10px; - margin-right: 12px; +export const ArrowStyle = styled.div<{ $opened: boolean }>` + border: 3px solid #7a8aa0; + border-bottom-width: 0; + border-left-color: transparent; + border-right-color: transparent; + + margin-right: 6px; + + transform: rotate(${({ $opened }) => ($opened ? 180 : 0)}deg); + transition: transform ${({ theme }) => theme.duration.norm} ease; `; diff --git a/shared/components/layout/header/components/header-wallet.tsx b/shared/components/layout/header/components/header-wallet.tsx index d6cb5f94f..5d2bd58e6 100644 --- a/shared/components/layout/header/components/header-wallet.tsx +++ b/shared/components/layout/header/components/header-wallet.tsx @@ -1,6 +1,5 @@ import { FC, useMemo } from 'react'; import { useRouter } from 'next/router'; -import { useAccount } from 'wagmi'; import { CHAINS as legacySDKCHAINS, getChainColor } from '@lido-sdk/constants'; @@ -9,7 +8,7 @@ import { useUserConfig } from 'config/user-config'; import { CHAINS } from 'consts/chains'; import { IPFSInfoBox } from 'features/ipfs/ipfs-info-box'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { Button, Connect } from 'shared/wallet'; import NoSSRWrapper from 'shared/components/no-ssr-wrapper'; @@ -24,28 +23,28 @@ import { ChainSwitcher } from './chain-switcher/chain-switcher'; const HeaderWallet: FC = () => { const router = useRouter(); - const { chainId, address } = useAccount(); const { defaultChain: defaultChainId } = useUserConfig(); - const { isDappActive } = useDappStatus(); + const { isDappActive, address, walletChainId } = useDappStatus(); - let chainName = legacySDKCHAINS[chainId || defaultChainId]; - if (!chainName && chainId === CHAINS.OptimismSepolia) { + let chainName = legacySDKCHAINS[walletChainId || defaultChainId]; + if (!chainName && walletChainId === CHAINS.OptimismSepolia) { chainName = 'Optimism Sepolia'; } const testNet = !( - chainId === legacySDKCHAINS.Mainnet || chainId === CHAINS.Optimism + walletChainId === legacySDKCHAINS.Mainnet || + walletChainId === CHAINS.Optimism ); const showNet = testNet && isDappActive; const queryTheme = router?.query?.theme; const chainColor = useMemo(() => { try { - return getChainColor(chainId || defaultChainId); + return getChainColor(walletChainId || defaultChainId); } catch { return getChainColor(defaultChainId); } - }, [chainId, defaultChainId]); + }, [walletChainId, defaultChainId]); return ( diff --git a/shared/components/tx-link-etherscan/tx-link-etherscan.tsx b/shared/components/tx-link-etherscan/tx-link-etherscan.tsx index c1ab75a23..19c3220f6 100644 --- a/shared/components/tx-link-etherscan/tx-link-etherscan.tsx +++ b/shared/components/tx-link-etherscan/tx-link-etherscan.tsx @@ -1,8 +1,8 @@ -import { useAccount } from 'wagmi'; import { Link } from '@lidofinance/lido-ui'; import { CHAINS } from 'consts/chains'; import { getEtherscanTxLink } from 'utils/get-etherscan-tx-link'; +import { useDappStatus } from 'modules/web3'; type TxLinkEtherscanProps = { text?: string; @@ -12,7 +12,7 @@ type TxLinkEtherscanProps = { export const TxLinkEtherscan = (props: TxLinkEtherscanProps) => { const { txHash, text = 'View on Etherscan', onClick } = props; - const { chainId } = useAccount(); + const { chainId } = useDappStatus(); if (!txHash) return null; diff --git a/shared/hook-form/controls/submit-button-hook-form.tsx b/shared/hook-form/controls/submit-button-hook-form.tsx index 0b4c6600c..88c693881 100644 --- a/shared/hook-form/controls/submit-button-hook-form.tsx +++ b/shared/hook-form/controls/submit-button-hook-form.tsx @@ -1,9 +1,7 @@ import { useFormState } from 'react-hook-form'; -import { useAccount } from 'wagmi'; import { ButtonIcon, Lock } from '@lidofinance/lido-ui'; -import { useIsSupportedChain } from 'shared/hooks/use-is-supported-chain'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { Connect, DisabledButton } from 'shared/wallet'; import { isValidationErrorTypeValidate } from '../validation/validation-error'; @@ -22,18 +20,16 @@ export const SubmitButtonHookForm: React.FC = ({ disabled: disabledProp, ...props }) => { - const { isConnected } = useAccount(); - const isSupportedChain = useIsSupportedChain(); - const { isDappActiveAndNetworksMatched } = useDappStatus(); + const { isDappActive, isSupportedChain, isWalletConnected } = useDappStatus(); const { isValidating, isSubmitting } = useFormState(); const { errors } = useFormState>(); - if (!isConnected) { + if (!isWalletConnected) { return ; } - if (!isSupportedChain || !isDappActiveAndNetworksMatched) { + if (!isSupportedChain || !isDappActive) { return {props.children}; } const disabled = diff --git a/shared/hook-form/form-controller/form-controller.tsx b/shared/hook-form/form-controller/form-controller.tsx index b1a799249..816b71cce 100644 --- a/shared/hook-form/form-controller/form-controller.tsx +++ b/shared/hook-form/form-controller/form-controller.tsx @@ -1,7 +1,7 @@ import { FC, PropsWithChildren, useEffect, useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useFormControllerContext } from './form-controller-context'; diff --git a/shared/hooks/index.ts b/shared/hooks/index.ts index 8ab813c00..996664993 100644 --- a/shared/hooks/index.ts +++ b/shared/hooks/index.ts @@ -1,7 +1,6 @@ export * from './useCopyToClipboard'; export * from './useLidoStats'; export * from './useLidoApr'; -export * from './useMaxGasPrice'; export * from './useWstethBySteth'; export * from './useStethByWsteth'; export * from './txCost'; diff --git a/shared/hooks/txCost.ts b/shared/hooks/txCost.ts index a0f28e645..5ebc1b70e 100644 --- a/shared/hooks/txCost.ts +++ b/shared/hooks/txCost.ts @@ -1,10 +1,11 @@ import type { BigNumber } from 'ethers'; import { useMemo } from 'react'; -import { useMaxGasPrice } from './useMaxGasPrice'; +import { useMaxGasPrice } from 'modules/web3'; import { useEthUsd } from './use-eth-usd'; -export const useTxCostInUsd = (gasLimit?: BigNumber) => { - const { maxGasPrice, ...gasSwr } = useMaxGasPrice(); +export const useTxCostInUsd = (gasLimit?: BigNumber, chainIdForce?: number) => { + const { maxGasPrice, ...gasSwr } = useMaxGasPrice(chainIdForce); + const ethAmount = useMemo( () => (maxGasPrice && gasLimit ? maxGasPrice.mul(gasLimit) : undefined), [gasLimit, maxGasPrice], diff --git a/shared/hooks/use-dapp-status.ts b/shared/hooks/use-dapp-status.ts deleted file mode 100644 index 74186ca5d..000000000 --- a/shared/hooks/use-dapp-status.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { useMemo } from 'react'; -import { useAccount } from 'wagmi'; - -import { isSDKSupportedL2Chain, LIDO_MULTICHAIN_CHAINS } from 'consts/chains'; - -import { useIsSupportedChain } from './use-is-supported-chain'; -import { useConfig } from 'config'; -import { useDappChain } from 'providers/dapp-chain'; - -export const useDappStatus = () => { - const { multiChainBanner } = useConfig().externalConfig; - const { chainId, isConnected: isWalletConnected } = useAccount(); - const isSupportedChain = useIsSupportedChain(); - const { isMatchDappChainAndWalletChain } = useDappChain(); - - const isLidoMultichainChain = useMemo( - () => - !!chainId && - !!LIDO_MULTICHAIN_CHAINS[chainId] && - multiChainBanner.includes(chainId), - [chainId, multiChainBanner], - ); - - const dappStatuses = useMemo(() => { - const isDappActive = chainId - ? isWalletConnected && isSupportedChain - : false; - - const isAccountActiveOnL1 = isDappActive && !isSDKSupportedL2Chain(chainId); - - const isAccountActiveOnL2 = isDappActive && isSDKSupportedL2Chain(chainId); - - const isDappActiveOnL1 = - isAccountActiveOnL1 && isMatchDappChainAndWalletChain(chainId); - - const isDappActiveOnL2 = - isAccountActiveOnL2 && isMatchDappChainAndWalletChain(chainId); - - const isDappActiveAndNetworksMatched = isDappActiveOnL1 || isDappActiveOnL2; - - return { - // TODO: rename to isAccountActive - isDappActive, - - isAccountActiveOnL1, - isAccountActiveOnL2, - - isDappActiveOnL1, - isDappActiveOnL2, - - // TODO: rename to isDappActive (see above) - isDappActiveAndNetworksMatched, - }; - }, [ - chainId, - isMatchDappChainAndWalletChain, - isSupportedChain, - isWalletConnected, - ]); - - return { - isWalletConnected, - isSupportedChain, - isLidoMultichainChain, - ...dappStatuses, - }; -}; diff --git a/shared/hooks/use-is-supported-chain.ts b/shared/hooks/use-is-supported-chain.ts deleted file mode 100644 index dad5301ad..000000000 --- a/shared/hooks/use-is-supported-chain.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useMemo } from 'react'; -import { useAccount } from 'wagmi'; -import { useUserConfig } from 'config/user-config'; - -export const useIsSupportedChain = () => { - const { chainId } = useAccount(); - const { supportedChainIds } = useUserConfig(); - - return useMemo(() => { - if (!chainId) return false; - - return supportedChainIds.indexOf(chainId) > -1; - }, [chainId, supportedChainIds]); -}; diff --git a/shared/hooks/use-lido-multichain-fallback-condition.ts b/shared/hooks/use-lido-multichain-fallback-condition.ts deleted file mode 100644 index 040b358ad..000000000 --- a/shared/hooks/use-lido-multichain-fallback-condition.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useDappStatus } from 'shared/hooks/use-dapp-status'; - -export const useLidoMultichainFallbackCondition = () => { - const { isLidoMultichainChain } = useDappStatus(); - const showLidoMultichainFallback = isLidoMultichainChain; - - return { - showLidoMultichainFallback, - }; -}; diff --git a/shared/hooks/use-token-address.ts b/shared/hooks/use-token-address.ts index 986524e60..429365d43 100644 --- a/shared/hooks/use-token-address.ts +++ b/shared/hooks/use-token-address.ts @@ -1,4 +1,4 @@ -import { useLidoSDK } from 'providers/lido-sdk'; +import { useLidoSDK } from 'modules/web3'; import { CHAINS, LIDO_L2_CONTRACT_ADDRESSES, diff --git a/shared/hooks/use-token-max-amount.ts b/shared/hooks/use-token-max-amount.ts index 59f5b6bb0..0c2411232 100644 --- a/shared/hooks/use-token-max-amount.ts +++ b/shared/hooks/use-token-max-amount.ts @@ -1,6 +1,6 @@ import { useMemo } from 'react'; import { BigNumber } from 'ethers'; -import { useMaxGasPrice } from './useMaxGasPrice'; +import { useMaxGasPrice } from 'modules/web3'; import { Zero } from '@ethersproject/constants'; type UseTokenMaxAmountArgs = { diff --git a/shared/hooks/use-wagmi-key.ts b/shared/hooks/use-wagmi-key.ts deleted file mode 100644 index f9c07ce6c..000000000 --- a/shared/hooks/use-wagmi-key.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useAccount } from 'wagmi'; -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 useWagmiKey = () => { - const { address, chainId } = useAccount(); - return `${address ?? 'NO_ADDRESS'}_${chainId ?? config.defaultChain}`; -}; diff --git a/shared/hooks/useApproveOnL1.ts b/shared/hooks/useApproveOnL1.ts index cbda6b72f..5abab4ee2 100644 --- a/shared/hooks/useApproveOnL1.ts +++ b/shared/hooks/useApproveOnL1.ts @@ -5,13 +5,12 @@ import { BigNumber } from '@ethersproject/bignumber'; import { getERC20Contract } from '@lido-sdk/contracts'; import { useSDK } from '@lido-sdk/react'; +import { sendTx, useAllowance, useTxConfirmation } from 'modules/web3'; + import { isContract } from 'utils/isContract'; 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, TransactionReceipt } from 'viem'; diff --git a/shared/hooks/useERC20PermitSignature.ts b/shared/hooks/useERC20PermitSignature.ts index cd4825f61..3695bacce 100644 --- a/shared/hooks/useERC20PermitSignature.ts +++ b/shared/hooks/useERC20PermitSignature.ts @@ -1,12 +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 { useSDK } from '@lido-sdk/react'; import { Erc20Abi, StethAbi } from '@lido-sdk/contracts'; +import { useDappStatus } from 'modules/web3'; export type GatherPermitSignatureResult = { v: number; @@ -55,8 +55,9 @@ export const useERC20PermitSignature = < tokenProvider, spender, }: UseERC20PermitSignatureProps): UseERC20PermitSignatureResult => { - const { address, chainId } = useAccount(); - const { providerWeb3 } = useSDK(); + const { address } = useDappStatus(); + + const { providerWeb3, chainId } = useSDK(); const gatherPermitSignature = useCallback( async (amount: BigNumber) => { diff --git a/shared/hooks/useIsMultisig.ts b/shared/hooks/useIsMultisig.ts deleted file mode 100644 index f3da9f737..000000000 --- a/shared/hooks/useIsMultisig.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useAccount } from 'wagmi'; -import { useIsContract } from 'shared/hooks/use-is-contract'; - -export const useIsMultisig = () => { - const { address } = useAccount(); - const { data: isMultisig, isLoading } = useIsContract(address ?? undefined); - return { isMultisig, isLoading }; -}; diff --git a/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx b/shared/transaction-modal/hooks/use-transaction-modal-stage.tsx index 3a6f50da5..b02ae1fe9 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 { useModalActions } from 'providers/modal-provider'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus } from 'modules/web3'; import { useTransactionModal, TransactionModal } from '../transaction-modal'; export type TransactionModalTransitStage = ( diff --git a/shared/wallet/button/button.tsx b/shared/wallet/button/button.tsx index 9b417a9bb..485db8e48 100644 --- a/shared/wallet/button/button.tsx +++ b/shared/wallet/button/button.tsx @@ -1,9 +1,8 @@ import { FC } from 'react'; -import { useAccount } from 'wagmi'; import { ButtonProps, useBreakpoint } from '@lidofinance/lido-ui'; import { FormatToken } from 'shared/formatters'; -import { useDappStatus } from 'shared/hooks/use-dapp-status'; +import { useDappStatus, useEthereumBalance } from 'modules/web3'; import { AddressBadge } from '../components/address-badge/address-badge'; import { useWalletModal } from '../wallet-modal/use-wallet-modal'; @@ -14,14 +13,12 @@ import { WalledButtonBalanceStyle, WalledButtonLoaderStyle, } from './styles'; -import { useEthereumBalance } from 'shared/hooks/use-balance'; export const Button: FC = (props) => { const { onClick, ...rest } = props; const isMobile = useBreakpoint('md'); - const { address } = useAccount(); - const { isDappActiveAndNetworksMatched } = useDappStatus(); + const { isDappActive, address } = useDappStatus(); const { openModal } = useWalletModal(); const { data: balance, isLoading } = useEthereumBalance(); @@ -32,9 +29,7 @@ export const Button: FC = (props) => { variant="text" color="secondary" onClick={() => openModal({})} - $isAddPaddingLeft={ - !isLoading && !isDappActiveAndNetworksMatched && !isMobile - } + $isAddPaddingLeft={!isLoading && !isDappActive && !isMobile} {...rest} > @@ -42,7 +37,7 @@ export const Button: FC = (props) => { {isLoading ? ( ) : ( - isDappActiveAndNetworksMatched && ( + isDappActive && ( { return ; @@ -60,17 +61,15 @@ export const CardBalance: WalletCardBalanceComponent = (props) => { ); }; -export const CardAccount: Component<'div', { account?: string | null }> = ( - props, -) => { - const { account, ...rest } = props; +export const CardAccount: Component<'div'> = (props) => { + const { address } = useDappStatus(); const { openModal } = useWalletModal(); return ( - + openModal({})} color="accent" /> diff --git a/shared/wallet/card/types.ts b/shared/wallet/card/types.ts index 854c970cc..24a196459 100644 --- a/shared/wallet/card/types.ts +++ b/shared/wallet/card/types.ts @@ -2,9 +2,7 @@ import { Component } from 'types'; import { BlockProps } from '@lidofinance/lido-ui'; import { FC } from 'react'; -export type WalletCardComponent = FC< - BlockProps & { error?: string | undefined } ->; +export type WalletCardComponent = FC; export type WalletCardRowComponent = Component<'div'>; diff --git a/shared/wallet/disabled-button/styles.tsx b/shared/wallet/disabled-button/styles.tsx index dd5cc27c2..d6c0cff82 100644 --- a/shared/wallet/disabled-button/styles.tsx +++ b/shared/wallet/disabled-button/styles.tsx @@ -6,6 +6,7 @@ export const ButtonStyle = styled((props) =>