From 98523da10356dd7563e9e8f8404e00decf323e1b Mon Sep 17 00:00:00 2001 From: Daniel Isaac Geslin Date: Tue, 24 Sep 2024 12:34:19 +0200 Subject: [PATCH 1/5] bug/binance-errors --- .../src/hooks/useBalanceChecker.ts | 62 +++++++++++++++++++ wormhole-connect/src/hooks/useIsMounted.ts | 12 ++++ .../WalletBalanceWarning.tsx | 58 +++++++++++++++++ .../v2/Bridge/ReviewTransaction/index.tsx | 25 +++++++- .../src/views/v2/Bridge/Routes/index.tsx | 2 +- 5 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 wormhole-connect/src/hooks/useBalanceChecker.ts create mode 100644 wormhole-connect/src/hooks/useIsMounted.ts create mode 100644 wormhole-connect/src/views/v2/Bridge/ReviewTransaction/WalletBalanceWarning.tsx diff --git a/wormhole-connect/src/hooks/useBalanceChecker.ts b/wormhole-connect/src/hooks/useBalanceChecker.ts new file mode 100644 index 000000000..9600fb3f9 --- /dev/null +++ b/wormhole-connect/src/hooks/useBalanceChecker.ts @@ -0,0 +1,62 @@ +import { useEffect, useState, useMemo } from 'react'; +import { amount, routes } from '@wormhole-foundation/sdk'; +import { getWalletConnection, TransferWallet } from 'utils/wallet'; +import config from 'config'; +import { useIsMounted } from './useIsMounted'; + +export const useBalanceChecker = ( + quote?: routes.Quote< + routes.Options, + routes.ValidatedTransferParams, + any + >, +): { + feeSymbol?: string; + isCheckingBalance: boolean; + isBalanceEnough: boolean; + walletBalance?: number; + networkCost?: number; +} => { + const [isCheckingBalance, setCheckingBalance] = useState(false); + const [state, setState] = useState<{ balance: number; cost: number } | null>( + null, + ); + const isMounted = useIsMounted(); + + const feeSymbol = useMemo(() => { + if (!quote?.relayFee?.token) return; + + return config.sdkConverter.findTokenConfigV1( + quote?.relayFee?.token, + Object.values(config.tokens), + )?.symbol; + }, [quote]); + + useEffect(() => { + setCheckingBalance(true); + (async () => { + try { + const wallet = getWalletConnection(TransferWallet.SENDING); + if (!wallet || !quote?.relayFee?.amount) return; + + const cost = amount.whole(quote.relayFee.amount); + const balance = parseFloat(await wallet.getBalance()); + + if (isMounted.current) setState({ balance, cost }); + } catch (e) { + console.error(e); + if (isMounted.current) setState(null); + } finally { + if (isMounted.current) setCheckingBalance(false); + } + })(); + }, [quote, isMounted]); + + return { + feeSymbol, + isCheckingBalance, + isBalanceEnough: !!state && state?.balance > state?.cost, + walletBalance: state?.balance, + networkCost: state?.cost, + }; +}; diff --git a/wormhole-connect/src/hooks/useIsMounted.ts b/wormhole-connect/src/hooks/useIsMounted.ts new file mode 100644 index 000000000..cd05253f3 --- /dev/null +++ b/wormhole-connect/src/hooks/useIsMounted.ts @@ -0,0 +1,12 @@ +import { MutableRefObject, useEffect, useRef } from 'react'; + +export const useIsMounted = (): MutableRefObject => { + const isMounted = useRef(true); + useEffect(() => { + isMounted.current = true; + return () => { + isMounted.current = false; + }; + }, []); + return isMounted; +}; diff --git a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/WalletBalanceWarning.tsx b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/WalletBalanceWarning.tsx new file mode 100644 index 000000000..87f9d0756 --- /dev/null +++ b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/WalletBalanceWarning.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import AlertBanner from 'components/v2/AlertBanner'; +import { Stack, Typography, useTheme } from '@mui/material'; +import { toFixedDecimals } from 'utils/balance'; + +export type WalletBalanceWarningProps = { + isCheckingBalance: boolean; + isBalanceEnough: boolean; + walletBalance?: number; + networkCost?: number; + symbol?: string; +}; + +export default function WalletBalanceWarning({ + isCheckingBalance, + isBalanceEnough, + walletBalance, + networkCost, + symbol, +}: WalletBalanceWarningProps) { + const theme = useTheme(); + const content = isCheckingBalance + ? `Checking if wallet balance is enough...` + : `Your wallet balance is not enough to cover the network cost. Please add more funds to your wallet.`; + + return ( + + + {!isBalanceEnough && !isCheckingBalance && ( + + + + Wallet balance + + + {!!walletBalance && + `${toFixedDecimals(walletBalance.toString(), 4)} ${symbol}`} + + + + + Network cost + + + {!!networkCost && + `${toFixedDecimals(networkCost.toString(), 4)} ${symbol}`} + + + + )} + + ); +} diff --git a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx index 71a9db0df..0a1ecf0da 100644 --- a/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx +++ b/wormhole-connect/src/views/v2/Bridge/ReviewTransaction/index.tsx @@ -43,6 +43,9 @@ import { toDecimals } from 'utils/balance'; import { useUSDamountGetter } from 'hooks/useUSDamountGetter'; import SendError from './SendError'; import { ERR_USER_REJECTED } from 'telemetry/types'; +import { useBalanceChecker } from 'hooks/useBalanceChecker'; +import WalletBalanceWarning from './WalletBalanceWarning'; +import { QuoteResult } from 'routes/operator'; const useStyles = makeStyles()((theme) => ({ container: { @@ -61,7 +64,7 @@ const useStyles = makeStyles()((theme) => ({ type Props = { onClose: () => void; - quotes: any; + quotes: Record; isFetchingQuotes: boolean; }; @@ -115,6 +118,14 @@ const ReviewTransaction = (props: Props) => { ? sdkAmount.whole(quote.destinationNativeGas) : undefined; + const { + isCheckingBalance, + feeSymbol, + isBalanceEnough, + walletBalance, + networkCost, + } = useBalanceChecker(quote); + const send = async () => { setSendError(undefined); @@ -326,7 +337,9 @@ const ReviewTransaction = (props: Props) => { return (