From 60d689b7163fc0a0c6c21e95f7012206d1dc4dd5 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Tue, 26 Mar 2024 15:13:09 -0400 Subject: [PATCH] Improve handling of validation errors such as gas estimation failures (#152) --- .../buttons/ConnectAwareSubmitButton.tsx | 2 +- src/features/transfer/TransferTokenForm.tsx | 9 ++++++- src/features/transfer/maxAmount.ts | 27 +++++++++---------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/components/buttons/ConnectAwareSubmitButton.tsx b/src/components/buttons/ConnectAwareSubmitButton.tsx index cc569146..835d3365 100644 --- a/src/components/buttons/ConnectAwareSubmitButton.tsx +++ b/src/components/buttons/ConnectAwareSubmitButton.tsx @@ -40,7 +40,7 @@ export function ConnectAwareSubmitButton({ chainName, text, cl // eslint-disable-next-line react-hooks/exhaustive-deps }, [setErrors, setTouched, errors, touched]); - useTimeout(clearErrors, 3000); + useTimeout(clearErrors, 3500); return ( diff --git a/src/features/transfer/TransferTokenForm.tsx b/src/features/transfer/TransferTokenForm.tsx index 1f6268bd..7718de28 100644 --- a/src/features/transfer/TransferTokenForm.tsx +++ b/src/features/transfer/TransferTokenForm.tsx @@ -274,6 +274,7 @@ function MaxButton({ balance, disabled }: { balance?: TokenAmount; disabled?: bo const onClick = async () => { if (!balance || isNullish(tokenIndex) || disabled) return; const maxAmount = await fetchMaxAmount({ balance, origin, destination, accounts }); + if (isNullish(maxAmount)) return; const decimalsAmount = maxAmount.getDecimalFormattedAmount(); const roundedAmount = new BigNumber(decimalsAmount).toFixed(4, BigNumber.ROUND_FLOOR); setFieldValue('amount', roundedAmount); @@ -421,6 +422,8 @@ function useFormInitialValues(): TransferFormValues { }, []); } +const insufficientFundsErrMsg = /insufficient.funds/i; + async function validateForm( values: TransferFormValues, accounts: Record, @@ -441,6 +444,10 @@ async function validateForm( return result; } catch (error) { logger.error('Error validating form', error); - return { form: errorToString(error) }; + let errorMsg = errorToString(error, 40); + if (insufficientFundsErrMsg.test(errorMsg)) { + errorMsg = 'Insufficient funds for gas fees'; + } + return { form: errorMsg }; } } diff --git a/src/features/transfer/maxAmount.ts b/src/features/transfer/maxAmount.ts index 8f7a5128..eac0d5f2 100644 --- a/src/features/transfer/maxAmount.ts +++ b/src/features/transfer/maxAmount.ts @@ -1,15 +1,15 @@ import { useMutation } from '@tanstack/react-query'; +import { toast } from 'react-toastify'; import { TokenAmount } from '@hyperlane-xyz/sdk'; -import { ProtocolType, timeout } from '@hyperlane-xyz/utils'; +import { ProtocolType } from '@hyperlane-xyz/utils'; import { getWarpCore } from '../../context/context'; import { logger } from '../../utils/logger'; +import { getChainMetadata } from '../chains/utils'; import { getAccountAddressAndPubKey } from '../wallet/hooks/multiProtocol'; import { AccountInfo } from '../wallet/hooks/types'; -const MAX_FETCH_TIMEOUT = 3000; // 3 seconds - interface FetchMaxParams { accounts: Record; balance: TokenAmount; @@ -28,18 +28,17 @@ async function fetchMaxAmount({ accounts, balance, destination, origin }: FetchM try { const { address, publicKey } = getAccountAddressAndPubKey(origin, accounts); if (!address) return balance; - const maxAmount = await timeout( - getWarpCore().getMaxTransferAmount({ - balance, - destination, - sender: address, - senderPubKey: await publicKey, - }), - MAX_FETCH_TIMEOUT, - ); + const maxAmount = await getWarpCore().getMaxTransferAmount({ + balance, + destination, + sender: address, + senderPubKey: await publicKey, + }); return maxAmount; } catch (error) { - logger.warn('Error or timeout fetching fee quotes for max amount', error); - return balance; + logger.warn('Error fetching fee quotes for max amount', error); + const chainName = getChainMetadata(origin).displayName; + toast.warn(`Cannot simulate transfer, ${chainName} native balance may be insufficient.`); + return undefined; } }