diff --git a/modules/blockChain/hooks/useTransactionSender.ts b/modules/blockChain/hooks/useTransactionSender.ts index 9465ec8a..16c38cad 100644 --- a/modules/blockChain/hooks/useTransactionSender.ts +++ b/modules/blockChain/hooks/useTransactionSender.ts @@ -7,6 +7,7 @@ import { PopulatedTransaction } from '@ethersproject/contracts' import { openWindow } from 'modules/shared/utils/openWindow' import { getGnosisSafeLink } from '../utils/getGnosisSafeLink' import { getEtherscanLink } from '@lido-sdk/helpers' +import { getErrorMessage } from 'modules/shared/utils/getErrorMessage' import { ToastError } from '@lidofinance/lido-ui' type PopulateFn = @@ -53,7 +54,7 @@ export function useTransactionSender( onError?.() setStatus('empty') console.error(error) - ToastError(error.message || (error as any).toString(), {}) + ToastError(getErrorMessage(error), {}) } }, [finish, populateTx, sendTransactionGnosisWorkaround, onError], diff --git a/modules/network/hooks/useSwr.ts b/modules/network/hooks/useSwr.ts index 9e30dd37..65aaec60 100644 --- a/modules/network/hooks/useSwr.ts +++ b/modules/network/hooks/useSwr.ts @@ -11,6 +11,7 @@ import { SWRInfiniteConfiguration, SWRInfiniteKeyLoader, } from 'swr/infinite' +import { getErrorMessage } from 'modules/shared/utils/getErrorMessage' export type SWRResponse = SWRResponseSource & { initialLoading: boolean @@ -19,7 +20,7 @@ export type SWRResponse = SWRResponseSource & { const defaultConfig = { onError: (error: any) => { console.error(error) - ToastError(error, {}) + ToastError(getErrorMessage(error), {}) }, errorRetryInterval: 100_000, focusThrottleInterval: 10_000, diff --git a/modules/shared/utils/getErrorMessage.ts b/modules/shared/utils/getErrorMessage.ts new file mode 100644 index 00000000..019bead3 --- /dev/null +++ b/modules/shared/utils/getErrorMessage.ts @@ -0,0 +1,90 @@ +export const ErrorMessageDisplayText = { + NOT_ENOUGH_ETHER: 'Not enough ether for gas.', + DENIED_SIG: 'User denied the transaction signature.', + SOMETHING_WRONG: 'Something went wrong.', + ENABLE_BLIND_SIGNING: + 'Please enable blind signing on your Ledger hardware wallet.', + LIMIT_REACHED: + 'Transaction could not be completed because stake limit is exhausted. Please wait until the stake limit restores and try again. Otherwise, you can swap your Ethereum on 1inch platform instantly.', +} as const + +// type safe error code extractor +const extractCodeFromError = ( + error: unknown, + shouldDig = true, +): number | string => { + // early exit on non object error + if (!error || typeof error != 'object') return 0 + + if ('reason' in error) { + if (typeof error.reason == 'string' && error.reason.includes('STAKE_LIMIT')) + return 'LIMIT_REACHED' + // TODO: error.reason more cases + } + + // sometimes we have error message but bad error code + if ('message' in error && typeof error.message == 'string') { + const normalizedMessage = error.message.toLowerCase() + if ( + normalizedMessage.includes('denied message signature') || + normalizedMessage.includes('transaction was rejected') || + normalizedMessage.includes('rejected the transaction') || + normalizedMessage.includes('rejected the request') + ) + return 'ACTION_REJECTED' + } + + // Ledger live errors + if ( + 'data' in error && + typeof error.data === 'object' && + Array.isArray(error.data) && + typeof error.data['0'] === 'object' && + typeof error.data['0'].message === 'string' + ) { + if (error.data['0'].message.toLowerCase().includes('rejected')) + return 'ACTION_REJECTED' + } + + if ('name' in error && typeof error.name == 'string') { + if (error.name.toLocaleLowerCase() === 'ethapppleaseenablecontractdata') + return 'ENABLE_BLIND_SIGNING' + } + if ('code' in error) { + if (typeof error.code === 'string') return error.code.toUpperCase() + if (typeof error.code == 'number') return error.code + } + + // errors are sometimes nested :( + if ('error' in error && shouldDig && error.error) { + return extractCodeFromError(error.error, false) + } + + return 0 +} + +export const getErrorMessage = (error: unknown) => { + try { + console.error('TX_ERROR:', { error, error_string: JSON.stringify(error) }) + } catch (e) { + console.error('TX_ERROR:', e) + } + + const code = extractCodeFromError(error) + switch (code) { + case -32000: + case 3: + case 'UNPREDICTABLE_GAS_LIMIT': + case 'INSUFFICIENT_FUNDS': + return ErrorMessageDisplayText.NOT_ENOUGH_ETHER + case 'ACTION_REJECTED': + case 4001: + return ErrorMessageDisplayText.DENIED_SIG + case 'LIMIT_REACHED': + return ErrorMessageDisplayText.LIMIT_REACHED + case 'ENABLE_BLIND_SIGNING': + return ErrorMessageDisplayText.ENABLE_BLIND_SIGNING + default: + return ErrorMessageDisplayText.SOMETHING_WRONG + } +}