From 7067a430908813c77fd0c8070e97fea571f39846 Mon Sep 17 00:00:00 2001 From: Jhonatan Gonzalez Date: Wed, 16 Oct 2024 16:00:30 +1100 Subject: [PATCH] [NO CHANGELOG][Add Funds Widget] Add validation for selected deliver to and pay with wallets (#2312) --- .../WalletDrawer/ConnectWalletDrawer.tsx | 14 ++++- .../WalletDrawer/DeliverToWalletDrawer.tsx | 26 ++++++-- .../WalletDrawer/PayWithWalletDrawer.tsx | 6 +- .../src/lib/connectEIP6963Provider.ts | 13 +++- .../components/AddressMissmatchDrawer.tsx | 62 +++++++++++++++++++ .../convertToNetworkChangeableProvider.ts | 6 ++ .../src/widgets/add-funds/hooks/useExecute.ts | 6 +- .../src/widgets/add-funds/views/AddFunds.tsx | 1 - .../src/widgets/add-funds/views/Review.tsx | 36 +++++++++-- 9 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 packages/checkout/widgets-lib/src/widgets/add-funds/components/AddressMissmatchDrawer.tsx create mode 100644 packages/checkout/widgets-lib/src/widgets/add-funds/functions/convertToNetworkChangeableProvider.ts diff --git a/packages/checkout/widgets-lib/src/components/WalletDrawer/ConnectWalletDrawer.tsx b/packages/checkout/widgets-lib/src/components/WalletDrawer/ConnectWalletDrawer.tsx index 8535d7f177..d5be4e7bfe 100644 --- a/packages/checkout/widgets-lib/src/components/WalletDrawer/ConnectWalletDrawer.tsx +++ b/packages/checkout/widgets-lib/src/components/WalletDrawer/ConnectWalletDrawer.tsx @@ -31,7 +31,7 @@ import { type ConnectWalletDrawerProps = { heading: string; visible: boolean; - onClose: () => void; + onClose: (address?: string) => void; onConnect?: ( provider: Web3Provider, providerInfo: EIP6963ProviderInfo @@ -45,6 +45,7 @@ type ConnectWalletDrawerProps = { label: string; rdns: string; }[]; + getShouldRequestWalletPermissions?: (providerInfo: EIP6963ProviderInfo) => boolean | undefined; }; export function ConnectWalletDrawer({ @@ -58,6 +59,7 @@ export function ConnectWalletDrawer({ bottomSlot, menuItemSize = 'small', disabledOptions = [], + getShouldRequestWalletPermissions, }: ConnectWalletDrawerProps) { const { providersState: { checkout }, @@ -110,6 +112,8 @@ export function ConnectWalletDrawer({ }, }); } + + return address; }; const handleOnWalletChangeEvent = async (event: WalletChangeEvent) => { @@ -155,17 +159,21 @@ export function ConnectWalletDrawer({ } } + let address: string | undefined; + // Proceed to connect selected provider + const shouldRequestWalletPermissions = getShouldRequestWalletPermissions?.(info); try { const { provider } = await connectEIP6963Provider( providerDetail, checkout, + shouldRequestWalletPermissions, ); // Identify connected wallet await identifyUser(identify, provider); // Store selected provider as fromProvider in context - setProviderInContext(provider, providerDetail.info); + address = await setProviderInContext(provider, providerDetail.info); // Notify successful connection onConnect?.(provider, providerDetail.info); @@ -188,7 +196,7 @@ export function ConnectWalletDrawer({ return; } - onClose(); + onClose(address); }; const retrySelectedWallet = () => { diff --git a/packages/checkout/widgets-lib/src/components/WalletDrawer/DeliverToWalletDrawer.tsx b/packages/checkout/widgets-lib/src/components/WalletDrawer/DeliverToWalletDrawer.tsx index 0660267bbd..d1fa4eb4d6 100644 --- a/packages/checkout/widgets-lib/src/components/WalletDrawer/DeliverToWalletDrawer.tsx +++ b/packages/checkout/widgets-lib/src/components/WalletDrawer/DeliverToWalletDrawer.tsx @@ -11,12 +11,13 @@ import { ViewActions, ViewContext, } from '../../context/view-context/ViewContext'; +import { useProvidersContext } from '../../context/providers-context/ProvidersContext'; type DeliverToWalletDrawerProps = { visible: boolean; - onClose: () => void; + onClose: (toAddress?: string) => void; walletOptions: EIP6963ProviderDetail[]; - onConnect: ( + onConnect?: ( providerType: 'from' | 'to', provider: Web3Provider, providerInfo: EIP6963ProviderInfo @@ -29,10 +30,17 @@ export function DeliverToWalletDrawer({ onConnect, walletOptions, }: DeliverToWalletDrawerProps) { + const { + providersState: { fromProviderInfo }, + } = useProvidersContext(); + const { viewDispatch } = useContext(ViewContext); - const handleOnConnect = (provider: Web3Provider, providerInfo: EIP6963ProviderInfo) => { - onConnect('to', provider, providerInfo); + const handleOnConnect = ( + provider: Web3Provider, + providerInfo: EIP6963ProviderInfo, + ) => { + onConnect?.('to', provider, providerInfo); }; const handleOnError = (errorType: ConnectEIP6963ProviderError) => { @@ -50,6 +58,13 @@ export function DeliverToWalletDrawer({ } }; + // Becuase wallets extensions don't support multiple wallet connections + // UX decides to have the user use the same wallet type they selected to pay with + // ie: Metamask to Metamsk, will send to same wallet address + const selectedSameFromWalletType = ( + providerInfo: EIP6963ProviderInfo, + ): boolean | undefined => (fromProviderInfo?.rdns !== providerInfo.rdns ? undefined : false); + return ( ); } diff --git a/packages/checkout/widgets-lib/src/components/WalletDrawer/PayWithWalletDrawer.tsx b/packages/checkout/widgets-lib/src/components/WalletDrawer/PayWithWalletDrawer.tsx index 859c545e5e..0599895094 100644 --- a/packages/checkout/widgets-lib/src/components/WalletDrawer/PayWithWalletDrawer.tsx +++ b/packages/checkout/widgets-lib/src/components/WalletDrawer/PayWithWalletDrawer.tsx @@ -9,9 +9,9 @@ import { SharedViews, ViewActions, ViewContext } from '../../context/view-contex type PayWithWalletDrawerProps = { visible: boolean; - onClose: () => void; + onClose: (fromAddress?: string) => void; onConnect: (providerType: 'from' | 'to', provider: Web3Provider, providerInfo: EIP6963ProviderInfo) => void; - onPayWithCard: () => void; + onPayWithCard?: () => void; walletOptions: EIP6963ProviderDetail[]; insufficientBalance?: boolean; showOnRampOption?: boolean; @@ -69,7 +69,7 @@ export function PayWithWalletDrawer({ emphasized onClick={() => { onClose(); - onPayWithCard(); + onPayWithCard?.(); }} > => { const web3Provider = new Web3Provider(providerDetail.provider as any); try { - const requestWalletPermissions = providerDetail.info.rdns === WalletProviderRdns.METAMASK; + const requestWalletPermissions = shouldRequestWalletPermissions + ?? providerDetail.info.rdns === WalletProviderRdns.METAMASK; const connectResult = await checkout.connect({ provider: web3Provider, requestWalletPermissions, @@ -40,7 +42,10 @@ export const connectEIP6963Provider = async ( ); if (isSanctioned) { - throw new CheckoutError('Sanctioned address', ConnectEIP6963ProviderError.SANCTIONED_ADDRESS); + throw new CheckoutError( + 'Sanctioned address', + ConnectEIP6963ProviderError.SANCTIONED_ADDRESS, + ); } addProviderListenersForWidgetRoot(connectResult.provider); @@ -51,7 +56,9 @@ export const connectEIP6963Provider = async ( } catch (error: CheckoutErrorType | ConnectEIP6963ProviderError | any) { switch (error.type) { case CheckoutErrorType.USER_REJECTED_REQUEST_ERROR: - throw new Error(ConnectEIP6963ProviderError.USER_REJECTED_REQUEST_ERROR); + throw new Error( + ConnectEIP6963ProviderError.USER_REJECTED_REQUEST_ERROR, + ); case ConnectEIP6963ProviderError.SANCTIONED_ADDRESS: throw new Error(ConnectEIP6963ProviderError.SANCTIONED_ADDRESS); default: diff --git a/packages/checkout/widgets-lib/src/widgets/add-funds/components/AddressMissmatchDrawer.tsx b/packages/checkout/widgets-lib/src/widgets/add-funds/components/AddressMissmatchDrawer.tsx new file mode 100644 index 0000000000..bd2a6e0648 --- /dev/null +++ b/packages/checkout/widgets-lib/src/widgets/add-funds/components/AddressMissmatchDrawer.tsx @@ -0,0 +1,62 @@ +import { + Body, Box, Button, Drawer, Heading, +} from '@biom3/react'; +import { WalletWarningHero } from '../../../components/Hero/WalletWarningHero'; + +export interface AddressMissmatchDrawerProps { + visible: boolean; + onClick: () => void; +} + +export function AddressMissmatchDrawer({ + visible, + onClick, +}: AddressMissmatchDrawerProps) { + return ( + + + + + + Oops! It seems your payment wallet has changed + + + You'll be ask to re-connect the same wallet you selected to pay with before proceeding. + + + + + + + + ); +} diff --git a/packages/checkout/widgets-lib/src/widgets/add-funds/functions/convertToNetworkChangeableProvider.ts b/packages/checkout/widgets-lib/src/widgets/add-funds/functions/convertToNetworkChangeableProvider.ts new file mode 100644 index 0000000000..63fe099fcd --- /dev/null +++ b/packages/checkout/widgets-lib/src/widgets/add-funds/functions/convertToNetworkChangeableProvider.ts @@ -0,0 +1,6 @@ +import { ethers } from 'ethers'; +import { Web3Provider } from '@ethersproject/providers'; + +export const convertToNetworkChangeableProvider = async ( + provider: Web3Provider, +): Promise => new ethers.providers.Web3Provider(provider.provider, 'any'); diff --git a/packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useExecute.ts b/packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useExecute.ts index 59e5d10914..b2cbcd9630 100644 --- a/packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useExecute.ts +++ b/packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useExecute.ts @@ -39,10 +39,7 @@ export const useExecute = (environment: Environment) => { showErrorHandover(errorType, { error }); }; - const convertToNetworkChangeableProvider = async ( - provider: Web3Provider, - ): Promise => new ethers.providers.Web3Provider(provider.provider, 'any'); - + // @TODO: Move to util function const checkProviderChain = async ( provider: Web3Provider, chainId: string, @@ -166,7 +163,6 @@ export const useExecute = (environment: Environment) => { }; return { - convertToNetworkChangeableProvider, checkProviderChain, getAllowance, approve, diff --git a/packages/checkout/widgets-lib/src/widgets/add-funds/views/AddFunds.tsx b/packages/checkout/widgets-lib/src/widgets/add-funds/views/AddFunds.tsx index 5450dc1e0a..674ae3f879 100644 --- a/packages/checkout/widgets-lib/src/widgets/add-funds/views/AddFunds.tsx +++ b/packages/checkout/widgets-lib/src/widgets/add-funds/views/AddFunds.tsx @@ -722,7 +722,6 @@ export function AddFunds({ visible={showDeliverToDrawer} walletOptions={walletOptions} onClose={() => setShowDeliverToDrawer(false)} - onConnect={handleWalletConnected} /> diff --git a/packages/checkout/widgets-lib/src/widgets/add-funds/views/Review.tsx b/packages/checkout/widgets-lib/src/widgets/add-funds/views/Review.tsx index fcd0f46268..1ba2bda776 100644 --- a/packages/checkout/widgets-lib/src/widgets/add-funds/views/Review.tsx +++ b/packages/checkout/widgets-lib/src/widgets/add-funds/views/Review.tsx @@ -28,10 +28,14 @@ import { Environment } from '@imtbl/config'; import { SimpleLayout } from '../../../components/SimpleLayout/SimpleLayout'; import { AddFundsContext } from '../context/AddFundsContext'; import { useRoutes } from '../hooks/useRoutes'; -import { AddFundsReviewData } from '../../../context/view-context/AddFundsViewContextTypes'; +import { + AddFundsReviewData, + AddFundsWidgetViews, +} from '../../../context/view-context/AddFundsViewContextTypes'; import { RiveStateMachineInput } from '../types'; import { useExecute } from '../hooks/useExecute'; import { + ViewActions, ViewContext, } from '../../../context/view-context/ViewContext'; import { SquidIcon } from '../components/SquidIcon'; @@ -53,12 +57,14 @@ import { useProvidersContext } from '../../../context/providers-context/Provider import { LoadingView } from '../../../views/loading/LoadingView'; import { getDurationFormatted } from '../functions/getDurationFormatted'; import { RouteFees } from '../components/RouteFees'; +import { AddressMissmatchDrawer } from '../components/AddressMissmatchDrawer'; import { getTotalRouteFees } from '../functions/getTotalRouteFees'; import { getRouteChains } from '../functions/getRouteChains'; import { getFormattedAmounts, getFormattedNumber, } from '../functions/getFormattedNumber'; +import { convertToNetworkChangeableProvider } from '../functions/convertToNetworkChangeableProvider'; interface ReviewProps { data: AddFundsReviewData; @@ -96,14 +102,13 @@ export function Review({ const [route, setRoute] = useState(); const [proceedDisabled, setProceedDisabled] = useState(true); const [showFeeBreakdown, setShowFeeBreakdown] = useState(false); - + const [showAddressMissmatchDrawer, setShowAddressMissmatchDrawer] = useState(false); const { getAmountData, getRoute } = useRoutes(); const { addHandover, closeHandover } = useHandover({ id: HandoverTarget.GLOBAL, }); const { - convertToNetworkChangeableProvider, checkProviderChain, getAllowance, approve, @@ -235,6 +240,13 @@ export function Review({ return; } + const currentFromAddress = await fromProvider.getSigner().getAddress(); + + if (currentFromAddress !== fromAddress) { + setShowAddressMissmatchDrawer(true); + return; + } + clearInterval(getRouteIntervalIdRef.current); setProceedDisabled(true); @@ -342,7 +354,6 @@ export function Review({ approve, showHandover, checkProviderChain, - convertToNetworkChangeableProvider, getAllowance, execute, closeHandover, @@ -662,7 +673,7 @@ export function Review({ )} - {!route && } + {!route && !showAddressMissmatchDrawer && } + { + setShowAddressMissmatchDrawer(false); + + viewDispatch({ + payload: { + type: ViewActions.UPDATE_VIEW, + view: { + type: AddFundsWidgetViews.ADD_FUNDS, + }, + }, + }); + }} + /> ); }