From 6b6877b39fd05bb01c07050d45ea27491ef0945e Mon Sep 17 00:00:00 2001 From: Alexander Khramov Date: Fri, 6 Oct 2023 16:54:14 +0300 Subject: [PATCH 1/8] feat: handle connection errors in AcceptTermsModal --- .../src/components/Terms/Terms.tsx | 2 +- .../components/WalletsModal/WalletsModal.tsx | 37 ++++-------- .../components/AcceptTermsModal.tsx | 60 +++++++++++++++++++ .../{ => components}/LidoModalLogo.tsx | 0 .../WalletsModal/components/index.ts | 2 + .../WalletsModalForEth/WalletsModalForEth.tsx | 4 +- .../src/context/acceptTermsModal.tsx | 6 ++ packages/core-react/src/context/reefKnot.tsx | 4 +- packages/web3-react/src/helpers/index.ts | 1 + .../src/helpers/unsupportedChainError.ts | 16 +++++ .../web3-react/src/hooks/useAutoConnect.ts | 20 +++++-- .../web3-react/src/hooks/useConnectorError.ts | 10 +++- 12 files changed, 127 insertions(+), 35 deletions(-) create mode 100644 packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx rename packages/connect-wallet-modal/src/components/WalletsModal/{ => components}/LidoModalLogo.tsx (100%) create mode 100644 packages/connect-wallet-modal/src/components/WalletsModal/components/index.ts create mode 100644 packages/web3-react/src/helpers/unsupportedChainError.ts diff --git a/packages/connect-wallet-modal/src/components/Terms/Terms.tsx b/packages/connect-wallet-modal/src/components/Terms/Terms.tsx index e511d276..aa62e898 100644 --- a/packages/connect-wallet-modal/src/components/Terms/Terms.tsx +++ b/packages/connect-wallet-modal/src/components/Terms/Terms.tsx @@ -3,7 +3,7 @@ import { Checkbox, CheckboxProps, Link } from '@reef-knot/ui-react'; import { TermsStyle, TermsTextStyle } from './styles'; import { Metrics } from '../WalletsModal'; -type WalletModalConnectTermsProps = Pick< +export type WalletModalConnectTermsProps = Pick< CheckboxProps, 'checked' | 'onChange' > & { metrics?: Metrics; termsLink: string; privacyNoticeLink: string }; diff --git a/packages/connect-wallet-modal/src/components/WalletsModal/WalletsModal.tsx b/packages/connect-wallet-modal/src/components/WalletsModal/WalletsModal.tsx index 569157c8..4e79eeb3 100644 --- a/packages/connect-wallet-modal/src/components/WalletsModal/WalletsModal.tsx +++ b/packages/connect-wallet-modal/src/components/WalletsModal/WalletsModal.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useContext, useState, ReactElement } from 'react'; -import { Button, Modal } from '@reef-knot/ui-react'; +import React, { useCallback, useContext, useState } from 'react'; +import { Modal } from '@reef-knot/ui-react'; import { AcceptTermsModalContext, LS_KEY_TERMS_ACCEPTANCE, @@ -9,12 +9,13 @@ import { ButtonsCommonProps, RequirementsData, } from './types'; -import { Terms } from '../Terms'; -import { WalletsButtonsContainer, CommonButtonsContainer } from './styles'; +import { Terms, WalletModalConnectTermsProps } from '../Terms'; +import { WalletsButtonsContainer } from './styles'; import { NOOP, useLocalStorage } from '../../helpers'; import { LedgerModal } from '../Ledger'; +import { AcceptTermsModal } from './components'; -export function WalletsModal(props: WalletsModalProps): ReactElement { +export function WalletsModal(props: WalletsModalProps) { const { onClose, shouldInvertWalletIcon = false, @@ -33,7 +34,7 @@ export function WalletsModal(props: WalletsModalProps): ReactElement { setTermsChecked((currentValue: boolean) => !currentValue); }, [setTermsChecked]); - const termsProps = { + const termsProps: WalletModalConnectTermsProps = { onChange: handleTermsToggle, checked: termsChecked, termsLink: termsLink || 'https://lido.fi/terms-of-use', @@ -111,25 +112,13 @@ export function WalletsModal(props: WalletsModalProps): ReactElement { if (acceptTermsModal?.isVisible) { return ( - - - - - - + termsProps={termsProps} + termsChecked={termsChecked} + onContinue={acceptTermsModal.onContinue} + error={acceptTermsModal.error} + /> ); } diff --git a/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx b/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx new file mode 100644 index 00000000..ab1ee605 --- /dev/null +++ b/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Button, Modal } from '@reef-knot/ui-react'; +import styled, { css } from '@reef-knot/ui-react/styled-wrapper'; +import { helpers } from '@reef-knot/web3-react'; +import { Terms, WalletModalConnectTermsProps } from '../../Terms'; +import { CommonButtonsContainer } from '../styles'; +import { useReefKnotContext } from '@reef-knot/core-react'; + +interface Props { + open: boolean; + termsProps: WalletModalConnectTermsProps; + termsChecked: boolean; + onContinue?: () => unknown; + error: Error | undefined; +} + +const ErrorBlock = styled.div` + ${({ theme: { fontSizesMap, spaceMap, borderRadiusesMap, colors } }) => css` + background: var(--lido-color-error); + color: var(--lido-color-text); + font-size: ${fontSizesMap.xxs}px; + line-height: 1.6em; + padding: ${spaceMap.lg}px; + margin-top: ${spaceMap.sm}px; + margin-bottom: ${spaceMap.md}px; + border-radius: ${borderRadiusesMap.lg}px; + `} +`; + +export const AcceptTermsModal = ({ + open, + termsProps, + termsChecked, + onContinue, + error, +}: Props) => { + const { chains: supportedChains } = useReefKnotContext(); + let errorMessage = error?.message; + if (error && error.name == 'UnsupportedChainIdError') { + errorMessage = helpers.getUnsupportedChainError(supportedChains).message; + } + + return ( + + + { error && {errorMessage} } + + + + + ); +}; diff --git a/packages/connect-wallet-modal/src/components/WalletsModal/LidoModalLogo.tsx b/packages/connect-wallet-modal/src/components/WalletsModal/components/LidoModalLogo.tsx similarity index 100% rename from packages/connect-wallet-modal/src/components/WalletsModal/LidoModalLogo.tsx rename to packages/connect-wallet-modal/src/components/WalletsModal/components/LidoModalLogo.tsx diff --git a/packages/connect-wallet-modal/src/components/WalletsModal/components/index.ts b/packages/connect-wallet-modal/src/components/WalletsModal/components/index.ts new file mode 100644 index 00000000..77750f7c --- /dev/null +++ b/packages/connect-wallet-modal/src/components/WalletsModal/components/index.ts @@ -0,0 +1,2 @@ +export * from './LidoModalLogo'; +export * from './AcceptTermsModal'; diff --git a/packages/connect-wallet-modal/src/components/WalletsModalForEth/WalletsModalForEth.tsx b/packages/connect-wallet-modal/src/components/WalletsModalForEth/WalletsModalForEth.tsx index c9f3e2c5..49f4e74a 100644 --- a/packages/connect-wallet-modal/src/components/WalletsModalForEth/WalletsModalForEth.tsx +++ b/packages/connect-wallet-modal/src/components/WalletsModalForEth/WalletsModalForEth.tsx @@ -100,9 +100,7 @@ function getWalletsButtons( }); } -export function WalletsModalForEth( - props: WalletsModalForEthProps, -): JSX.Element { +export function WalletsModalForEth(props: WalletsModalForEthProps) { const { walletDataList } = useReefKnotContext(); return ( diff --git a/packages/core-react/src/context/acceptTermsModal.tsx b/packages/core-react/src/context/acceptTermsModal.tsx index 71e577a4..caa7ea21 100644 --- a/packages/core-react/src/context/acceptTermsModal.tsx +++ b/packages/core-react/src/context/acceptTermsModal.tsx @@ -6,6 +6,8 @@ export type AcceptTermsModalContextValue = { setVisible: React.Dispatch>; onContinue: () => void; setOnContinue: React.Dispatch void>>; + error?: Error, + setError: React.Dispatch>; }; }; @@ -28,6 +30,8 @@ export const AcceptTermsModalContextProvider: FC = ({ children }) => { () => onContinueDefaultValue, ); + const [error, setError] = useState(undefined); + const contextValue = useMemo( () => ({ acceptTermsModal: { @@ -35,6 +39,8 @@ export const AcceptTermsModalContextProvider: FC = ({ children }) => { setVisible: setIsAcceptTermsModalVisible, onContinue: onAcceptTermsModalContinue, setOnContinue: setOnAcceptTermsModalContinue, + error, + setError }, }), [isAcceptTermsModalVisible, onAcceptTermsModalContinue], diff --git a/packages/core-react/src/context/reefKnot.tsx b/packages/core-react/src/context/reefKnot.tsx index 8aaf7268..7a19f675 100644 --- a/packages/core-react/src/context/reefKnot.tsx +++ b/packages/core-react/src/context/reefKnot.tsx @@ -15,6 +15,7 @@ export interface ReefKnotContextProps { export type ReefKnotContextValue = { rpc: Record; walletDataList: WalletAdapterData[]; + chains: Chain[]; }; export const ReefKnotContext = createContext({} as ReefKnotContextValue); @@ -37,8 +38,9 @@ export const ReefKnot: FC = ({ () => ({ rpc, walletDataList, + chains, }), - [rpc, walletDataList], + [rpc, walletDataList, chains], ); return ( diff --git a/packages/web3-react/src/helpers/index.ts b/packages/web3-react/src/helpers/index.ts index 2d175d1a..755d2d22 100644 --- a/packages/web3-react/src/helpers/index.ts +++ b/packages/web3-react/src/helpers/index.ts @@ -3,3 +3,4 @@ export * from './ua'; export * from './openWindow'; export { default as isUrl } from './isUrl'; export * from './interceptLedgerError'; +export * from './unsupportedChainError'; diff --git a/packages/web3-react/src/helpers/unsupportedChainError.ts b/packages/web3-react/src/helpers/unsupportedChainError.ts new file mode 100644 index 00000000..dce769fc --- /dev/null +++ b/packages/web3-react/src/helpers/unsupportedChainError.ts @@ -0,0 +1,16 @@ +import { Chain } from 'wagmi/chains'; + +export const getUnsupportedChainError = (supportedChains: Chain[]) => { + // Get names of supported chains to suggest them in case of "unsupported network" error + const supportedChainsNames = (() => { + const chains = supportedChains + .map(({ name }) => name) + .filter((chainName) => chainName !== 'unknown'); + const lastChain = chains.pop(); + return [chains.join(', '), lastChain].filter((chain) => chain).join(' or '); + })(); + + return new Error( + `Unsupported chain. Please switch to ${supportedChainsNames} in your wallet and restart the page.`, + ); +}; diff --git a/packages/web3-react/src/hooks/useAutoConnect.ts b/packages/web3-react/src/hooks/useAutoConnect.ts index 6ebb4b54..b932f0ab 100644 --- a/packages/web3-react/src/hooks/useAutoConnect.ts +++ b/packages/web3-react/src/hooks/useAutoConnect.ts @@ -52,11 +52,21 @@ export const useEagerConnector = (connectors: ConnectorsContextValue) => { if (!connector) return; const connectWallet = async () => { - await activate(connector, undefined, true); - // Hide the modal if a user approved the connection in a wallet's UI. - // If a user rejects the connection, then an error is thrown - // and the following code is not being reached, the modal stays visible. - acceptTermsModal.setVisible?.(false); + let error: Error | undefined = undefined; + try { + await activate(connector, undefined, true); + } catch (e) { + error = e as Error; + } + if (shouldAutoConnectApp) { + if (!termsAccepted || error) { + acceptTermsModal.setError?.(error); + acceptTermsModal.setVisible?.(true); + } else { + acceptTermsModal.setVisible?.(false); + acceptTermsModal.setError?.(undefined); + } + } }; let termsAccepted = false; diff --git a/packages/web3-react/src/hooks/useConnectorError.ts b/packages/web3-react/src/hooks/useConnectorError.ts index f440d384..9dd37c4c 100644 --- a/packages/web3-react/src/hooks/useConnectorError.ts +++ b/packages/web3-react/src/hooks/useConnectorError.ts @@ -1,15 +1,23 @@ import { useWeb3 } from './useWeb3'; -import { interceptLedgerError } from '../helpers'; +import { useReefKnotContext } from '@reef-knot/core-react'; +import { interceptLedgerError, getUnsupportedChainError } from '../helpers'; import { useConnectorInfo } from './useConnectorInfo'; +import { useSupportedChains } from './useSupportedChains'; export const useConnectorError = (): Error | undefined => { const { error } = useWeb3(); const { isLedger } = useConnectorInfo(); + const { isUnsupported } = useSupportedChains(); + const { chains: supportedChains } = useReefKnotContext(); if (!error) { return; } + if (isUnsupported) { + return getUnsupportedChainError(supportedChains); + } + if (isLedger) { return interceptLedgerError(error); } From 9990c2fe907964ff750825b6a9f72ef500c6e54d Mon Sep 17 00:00:00 2001 From: Alexander Khramov Date: Fri, 20 Oct 2023 18:31:50 +0300 Subject: [PATCH 2/8] feat(web3-react): add holesky to wagmiChainsArray --- packages/web3-react/src/context/web3.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/web3-react/src/context/web3.tsx b/packages/web3-react/src/context/web3.tsx index 0981af0b..0dfda39d 100644 --- a/packages/web3-react/src/context/web3.tsx +++ b/packages/web3-react/src/context/web3.tsx @@ -9,7 +9,7 @@ import { CHAINS } from '@lido-sdk/constants'; import { getStaticRpcBatchProvider } from '@lido-sdk/providers'; import { ProviderSDK as ProviderSDKBase } from '@lido-sdk/react'; import { useWeb3React, Web3ReactProvider } from '@web3-react/core'; -import { ReefKnot } from '@reef-knot/core-react'; +import { holesky, ReefKnot } from '@reef-knot/core-react'; import { useAccount } from 'wagmi'; import * as wagmiChains from 'wagmi/chains'; import { SWRConfiguration } from 'swr'; @@ -122,7 +122,7 @@ const ProviderWeb3: FC = (props) => { } = props; const { defaultChainId, supportedChainIds } = props; const connectorsProps = { rpc, appName, appLogoUrl, defaultChainId }; - const wagmiChainsArray = Object.values(wagmiChains); + const wagmiChainsArray = Object.values({ ...wagmiChains, holesky }); const supportedWagmiChains = wagmiChainsArray.filter((chain) => supportedChainIds.includes(chain.id), ); From 0474616b97c96d66ab05b6e0c68e60c5af1e4e53 Mon Sep 17 00:00:00 2001 From: Alexander Khramov Date: Fri, 20 Oct 2023 18:32:34 +0300 Subject: [PATCH 3/8] feat(demo-react): add holesky --- apps/demo-react/config/chains.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/demo-react/config/chains.ts b/apps/demo-react/config/chains.ts index 92bb3f44..f03fd571 100644 --- a/apps/demo-react/config/chains.ts +++ b/apps/demo-react/config/chains.ts @@ -1,4 +1,5 @@ export enum CHAINS { Mainnet = 1, Goerli = 5, + Holesky = 17000, } From 670ef8b737e83c6d3b03798808ea81ec7452aa27 Mon Sep 17 00:00:00 2001 From: Alexander Khramov Date: Fri, 20 Oct 2023 18:32:43 +0300 Subject: [PATCH 4/8] fix(demo-react): typo --- apps/demo-react/components/ProviderWeb3WithProps.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/demo-react/components/ProviderWeb3WithProps.tsx b/apps/demo-react/components/ProviderWeb3WithProps.tsx index 25d1bdb1..fd6ccde5 100644 --- a/apps/demo-react/components/ProviderWeb3WithProps.tsx +++ b/apps/demo-react/components/ProviderWeb3WithProps.tsx @@ -12,7 +12,7 @@ const supportedChains = [holesky, mainnet, goerli]; const supportedChainsIds = supportedChains.map((chain) => chain.id); const defaultChainId = holesky.id; -const jsonRcpBatchProvider = (chain: Chain) => ({ +const jsonRpcBatchProvider = (chain: Chain) => ({ provider: () => getStaticRpcBatchProvider(chain.id, getRPCPath(chain.id), undefined, 12000), chain, @@ -20,7 +20,7 @@ const jsonRcpBatchProvider = (chain: Chain) => ({ const { chains, provider, webSocketProvider } = configureChains( supportedChains, - [jsonRcpBatchProvider], + [jsonRpcBatchProvider], ); const connectors = getConnectors({ From 5a9c6e154a24ce158642e63d10b4fa2731310266 Mon Sep 17 00:00:00 2001 From: Alexander Khramov Date: Wed, 25 Oct 2023 12:06:56 +0300 Subject: [PATCH 5/8] style: fix eslint errors --- .../components/WalletsModal/components/AcceptTermsModal.tsx | 4 ++-- packages/core-react/src/context/acceptTermsModal.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx b/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx index ab1ee605..10d9d9a7 100644 --- a/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx +++ b/packages/connect-wallet-modal/src/components/WalletsModal/components/AcceptTermsModal.tsx @@ -15,7 +15,7 @@ interface Props { } const ErrorBlock = styled.div` - ${({ theme: { fontSizesMap, spaceMap, borderRadiusesMap, colors } }) => css` + ${({ theme: { fontSizesMap, spaceMap, borderRadiusesMap } }) => css` background: var(--lido-color-error); color: var(--lido-color-text); font-size: ${fontSizesMap.xxs}px; @@ -43,7 +43,7 @@ export const AcceptTermsModal = ({ return ( - { error && {errorMessage} } + {error && {errorMessage} }