From 1594a0745babf2e275ce88c5b55627e1934b4134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Thu, 30 Jan 2025 20:32:46 +0100 Subject: [PATCH 1/4] chore: improve swap failure messaging & log them to Sentry --- .../SwapProvider/SwapProvider.test.tsx | 24 +++- src/contexts/SwapProvider/SwapProvider.tsx | 70 +++++----- src/contexts/SwapProvider/models.ts | 10 ++ src/contexts/SwapProvider/swap-utils.ts | 129 +++++++++++++++--- src/hooks/useErrorMessage.ts | 47 ++++++- src/localization/locales/en/translation.json | 14 +- src/monitoring/sentryCaptureException.ts | 2 + src/pages/Swap/Swap.tsx | 24 +++- src/utils/errors/errorCodes.ts | 4 + 9 files changed, 265 insertions(+), 59 deletions(-) diff --git a/src/contexts/SwapProvider/SwapProvider.test.tsx b/src/contexts/SwapProvider/SwapProvider.test.tsx index 4bd3e4d21..fcae24b2a 100644 --- a/src/contexts/SwapProvider/SwapProvider.test.tsx +++ b/src/contexts/SwapProvider/SwapProvider.test.tsx @@ -13,12 +13,19 @@ import { useWalletContext } from '../WalletProvider'; import { ChainId } from '@avalabs/core-chains-sdk'; import { Contract } from 'ethers'; import Big from 'big.js'; -import { GetRateParams, SwapContextAPI, SwapParams } from './models'; +import { + GetRateParams, + SwapContextAPI, + SwapErrorCode, + SwapParams, +} from './models'; import { SwapContextProvider, useSwapContext } from './SwapProvider'; import { useNetworkFeeContext } from '../NetworkFeeProvider'; import { FeatureGates } from '@src/background/services/featureFlags/models'; import { SecretType } from '@src/background/services/secrets/models'; import { RpcMethod } from '@avalabs/vm-module-types'; +import * as swapUtils from './swap-utils'; +import { CommonError } from '@src/utils/errors'; const API_URL = 'https://apiv5.paraswap.io'; const ACTIVE_ACCOUNT_ADDRESS = 'addressC'; @@ -122,6 +129,10 @@ describe('contexts/SwapProvider', () => { jest.resetAllMocks(); jest.useRealTimers(); + jest + .spyOn(swapUtils, 'getParaswapSpender') + .mockResolvedValue('0xParaswapContractAddress'); + jest.spyOn(global, 'fetch').mockResolvedValue({ json: async () => ({}), ok: true, @@ -200,7 +211,7 @@ describe('contexts/SwapProvider', () => { const { getRate } = getSwapProvider(); await expect(getRate(buildGetRateParams())).rejects.toThrow( - 'Unsupported network', + swapUtils.swapError(CommonError.UnknownNetwork), ); }, ); @@ -217,7 +228,7 @@ describe('contexts/SwapProvider', () => { const { getRate } = getSwapProvider(); await expect(getRate(buildGetRateParams())).rejects.toThrow( - 'Account address missing', + swapUtils.swapError(CommonError.NoActiveAccount), ); }); }); @@ -308,7 +319,7 @@ describe('contexts/SwapProvider', () => { const { getRate } = getSwapProvider(); await expect(getRate(buildGetRateParams())).rejects.toThrow( - 'Invalid tokens', + swapUtils.swapError(CommonError.Unknown, new Error('Invalid tokens')), ); expect(global.fetch).toHaveBeenCalledTimes(1); @@ -379,7 +390,10 @@ describe('contexts/SwapProvider', () => { const { swap } = getSwapProvider(); await expect(swap(params)).rejects.toThrow( - `Missing parameter: ${paramName}`, + swapUtils.swapError( + SwapErrorCode.MissingParams, + new Error(`Missing parameter: ${paramName}`), + ), ); }); diff --git a/src/contexts/SwapProvider/SwapProvider.tsx b/src/contexts/SwapProvider/SwapProvider.tsx index 3b9a5134d..0ab8aaa98 100644 --- a/src/contexts/SwapProvider/SwapProvider.tsx +++ b/src/contexts/SwapProvider/SwapProvider.tsx @@ -29,6 +29,7 @@ import { BuildTxParams, GetSwapPropsParams, ValidTransactionResponse, + SwapErrorCode, } from './models'; import Joi from 'joi'; import { isAPIError } from '@src/pages/Swap/utils'; @@ -37,12 +38,17 @@ import { checkForErrorsInBuildTxResult, checkForErrorsInGetRateResult, ensureAllowance, + getParaswapSpender, hasEnoughAllowance, - throwError, + swapError, validateParams, } from './swap-utils'; import { assert, assertPresent } from '@src/utils/assertions'; -import { CommonError } from '@src/utils/errors'; +import { + CommonError, + isUserRejectionError, + wrapError, +} from '@src/utils/errors'; import { useWalletContext } from '../WalletProvider'; import { SecretType } from '@src/background/services/secrets/models'; @@ -92,10 +98,10 @@ export function SwapContextProvider({ children }: { children: any }) { swapSide, }: GetRateParams) => { if (!activeNetwork || activeNetwork.isTestnet) { - throw new Error(`Unsupported network: ${activeNetwork?.chainId}`); + throw swapError(CommonError.UnknownNetwork); } if (!activeAccount || !activeAccount.addressC) { - throw new Error('Account address missing'); + throw swapError(CommonError.NoActiveAccount); } if (!isFlagEnabled(FeatureGates.SWAP)) { @@ -138,21 +144,6 @@ export function SwapContextProvider({ children }: { children: any }) { [activeAccount, activeNetwork, isFlagEnabled, paraswap], ); - const getParaswapSpender = useCallback(async () => { - if (!isFlagEnabled(FeatureGates.SWAP)) { - throw new Error(`Feature (SWAP) is currently unavailable`); - } - - const response = await fetch( - `${(paraswap as any).apiURL}/adapters/contracts?network=${ - ChainId.AVALANCHE_MAINNET_ID - }`, - ); - - const result = await response.json(); - return result.TokenTransferProxy; - }, [paraswap, isFlagEnabled]); - const buildTx = useCallback( async ({ network, @@ -201,7 +192,8 @@ export function SwapContextProvider({ children }: { children: any }) { 'Content-Type': 'application/json', }, body: JSON.stringify(txConfig), - }); + }).catch(wrapError(swapError(CommonError.NetworkError))); + const transactionParamsOrError: Transaction | APIError = await response.json(); const validationResult = responseSchema.validate( @@ -210,9 +202,15 @@ export function SwapContextProvider({ children }: { children: any }) { if (!response.ok || validationResult.error) { if (isAPIError(transactionParamsOrError)) { - throw new Error(transactionParamsOrError.message); + throw swapError( + SwapErrorCode.ApiError, + new Error(transactionParamsOrError.message), + ); } - throw new Error('Invalid transaction params'); + throw swapError( + SwapErrorCode.UnexpectedApiResponse, + validationResult.error, + ); } const txPayload = validationResult.value; @@ -326,12 +324,12 @@ export function SwapContextProvider({ children }: { children: any }) { return { srcTokenAddress: srcToken === nativeToken ? ETHER_ADDRESS : srcToken, destTokenAddress: destToken === nativeToken ? ETHER_ADDRESS : destToken, - spender: await getParaswapSpender(), + spender: await getParaswapSpender((paraswap as any).apiURL), sourceAmount, destinationAmount, }; }, - [getParaswapSpender], + [paraswap], ); /** @@ -437,8 +435,10 @@ export function SwapContextProvider({ children }: { children: any }) { }), ); - if (batchSignError || !txHashes) { - return throwError(batchSignError); + if (isUserRejectionError(batchSignError)) { + throw batchSignError; + } else if (batchSignError || !txHashes) { + throw swapError(CommonError.UnableToSign, batchSignError); } swapTxHash = txHashes[txHashes.length - 1]; @@ -450,8 +450,10 @@ export function SwapContextProvider({ children }: { children: any }) { }), ); - if (signError || !txHash) { - return throwError(signError); + if (isUserRejectionError(signError)) { + throw signError; + } else if (signError || !txHash) { + throw swapError(CommonError.UnableToSign, signError); } swapTxHash = txHash; @@ -559,7 +561,7 @@ export function SwapContextProvider({ children }: { children: any }) { ); if (txBuildDataError || !swapTx) { - throw new Error(`Data Error: ${txBuildDataError}`); + throw swapError(SwapErrorCode.CannotBuildTx, txBuildDataError); } const [swapTxHash, signError] = await resolve( @@ -569,8 +571,10 @@ export function SwapContextProvider({ children }: { children: any }) { }), ); - if (signError || !swapTxHash) { - return throwError(signError); + if (isUserRejectionError(signError)) { + throw signError; + } else if (signError || !swapTxHash) { + throw swapError(CommonError.UnableToSign, signError); } notifyOnSwapResult({ @@ -599,6 +603,10 @@ export function SwapContextProvider({ children }: { children: any }) { const swap = useCallback( async (params: SwapParams) => { + if (!isFlagEnabled(FeatureGates.SWAP)) { + throw new Error(`Feature (SWAP) is currently unavailable`); + } + const isOneClickSwapEnabled = isFlagEnabled(FeatureGates.ONE_CLICK_SWAP); const isOneClickSwapSupported = walletDetails?.type === SecretType.Mnemonic || diff --git a/src/contexts/SwapProvider/models.ts b/src/contexts/SwapProvider/models.ts index 7daed09ee..5acd370e4 100644 --- a/src/contexts/SwapProvider/models.ts +++ b/src/contexts/SwapProvider/models.ts @@ -100,3 +100,13 @@ export type ValidTransactionResponse = { gas?: string; gasPrice?: string; }; + +export enum SwapErrorCode { + MissingParams = 'missing-params', + CannotFetchAllowance = 'cannot-fetch-allowance', + MissingContractMethod = 'missing-contract-method', + ApiError = 'api-error', + UnknownSpender = 'unknown-spender', + UnexpectedApiResponse = 'unexpected-api-response', + CannotBuildTx = 'cannot-build-tx', +} diff --git a/src/contexts/SwapProvider/swap-utils.ts b/src/contexts/SwapProvider/swap-utils.ts index 26553e2c7..552ddd4be 100644 --- a/src/contexts/SwapProvider/swap-utils.ts +++ b/src/contexts/SwapProvider/swap-utils.ts @@ -3,14 +3,22 @@ import { Contract } from 'ethers'; import { ChainId } from '@avalabs/core-chains-sdk'; import { RpcMethod } from '@avalabs/vm-module-types'; import { JsonRpcBatchInternal } from '@avalabs/core-wallets-sdk'; +import { ethErrors } from 'eth-rpc-errors'; import ERC20 from '@openzeppelin/contracts/build/contracts/ERC20.json'; +import { + CommonError, + isUserRejectionError, + isWrappedError, + wrapError, +} from '@src/utils/errors'; import { resolve } from '@src/utils/promiseResolver'; import { RequestHandlerType } from '@src/background/connections/models'; import { PARASWAP_RETRYABLE_ERRORS, ParaswapPricesResponse, + SwapErrorCode, SwapParams, hasParaswapError, } from './models'; @@ -30,35 +38,59 @@ export function validateParams( } = params; if (!srcToken) { - throw new Error('Missing parameter: srcToken'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: srcToken'), + ); } if (!destToken) { - throw new Error('Missing parameter: destToken'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: destToken'), + ); } if (!srcAmount) { - throw new Error('Missing parameter: srcAmount'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: srcAmount'), + ); } if (!srcDecimals) { - throw new Error('Missing parameter: srcDecimals'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: srcDecimals'), + ); } if (!destDecimals) { - throw new Error('Missing parameter: destDecimals'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: destDecimals'), + ); } if (!destAmount) { - throw new Error('Missing parameter: destAmount'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: destAmount'), + ); } if (!priceRoute) { - throw new Error('Missing parameter: priceRoute'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: priceRoute'), + ); } if (!slippage) { - throw new Error('Missing parameter: slippage'); + throw swapError( + SwapErrorCode.MissingParams, + new Error('Missing parameter: slippage'), + ); } return { @@ -103,9 +135,7 @@ export async function buildApprovalTx({ ); if (approvalGasLimitError) { - throw new Error( - `Unable to estimate gas limit for allowance approval. Error: ${approvalGasLimitError}`, - ); + throw swapError(CommonError.UnableToEstimateGas, approvalGasLimitError); } return { @@ -130,7 +160,10 @@ export async function hasEnoughAllowance({ const contract = new Contract(tokenAddress, ERC20.abi, provider); if (!contract.allowance) { - throw new Error(`Allowance Conract Error`); + throw swapError( + SwapErrorCode.MissingContractMethod, + new Error(`Contract Error: allowance method is not available`), + ); } const [allowance, allowanceError] = await resolve( @@ -138,7 +171,7 @@ export async function hasEnoughAllowance({ ); if (allowanceError) { - throw new Error(`Allowance Fetching Error: ${allowanceError}`); + throw swapError(SwapErrorCode.CannotFetchAllowance, allowanceError); } return allowance >= requiredAmount; @@ -186,17 +219,45 @@ export async function ensureAllowance({ }), ); - if (signError) { - throwError(signError); + if (isUserRejectionError(signError)) { + throw signError; + } else if (signError) { + throw swapError(CommonError.UnableToSign, signError); } } -export const throwError = (err: string | unknown): never => { +const normalizeError = (err: unknown) => { + if (isWrappedError(err)) { + return err; + } + + if (err instanceof Error) { + return err; + } + if (typeof err === 'string') { - throw new Error(err); + return new Error(err); } - throw err; + return new Error((err as any)?.message ?? 'Unknown error'); +}; + +export const swapError = ( + errorCode: CommonError | SwapErrorCode, + originalError?: unknown, +) => { + if (isWrappedError(originalError)) { + return originalError; + } + + return ethErrors.rpc.internal({ + data: { + reason: errorCode, + originalError: originalError + ? normalizeError(originalError) + : new Error('Unknown swap error'), + }, + }); }; export function checkForErrorsInGetRateResult( @@ -220,9 +281,9 @@ export function checkForErrorsInGetRateResult( // we need to propagate the error so we're able to show an approriate // message in the UI. } else if (isFetchError) { - throw response; + throw swapError(CommonError.NetworkError, response); } else { - throw new Error(response.error); + throw swapError(CommonError.Unknown, new Error(response.error)); } } @@ -238,3 +299,31 @@ export function checkForErrorsInBuildTxResult(result: Transaction | APIError) { result instanceof Error ); } + +export const getParaswapSpender = async (paraswapApiUrl: string) => { + const response = await fetch( + `${paraswapApiUrl}/adapters/contracts?network=${ + ChainId.AVALANCHE_MAINNET_ID + }`, + ).catch(wrapError(swapError(CommonError.NetworkError))); + + const result = await response + .json() + .catch( + wrapError( + swapError( + SwapErrorCode.UnexpectedApiResponse, + new Error('Failed to /adapters/contracts response'), + ), + ), + ); + + if (!result.TokenTransferProxy) { + throw swapError( + SwapErrorCode.UnknownSpender, + new Error('Missing TokenTransferProxy address'), + ); + } + + return result.TokenTransferProxy; +}; diff --git a/src/hooks/useErrorMessage.ts b/src/hooks/useErrorMessage.ts index 11b56fd8a..10ce9ca90 100644 --- a/src/hooks/useErrorMessage.ts +++ b/src/hooks/useErrorMessage.ts @@ -7,6 +7,7 @@ import { CommonError, RpcErrorCode, isWrappedError } from '@src/utils/errors'; import { UnifiedBridgeError } from '@src/background/services/unifiedBridge/models'; import { KeystoreError } from '@src/utils/keystore/models'; import { SeedphraseImportError } from '@src/background/services/wallet/handlers/models'; +import { SwapErrorCode } from '@src/contexts/SwapProvider/models'; type ErrorTranslation = { title: string; @@ -112,6 +113,42 @@ export const useErrorMessage = () => { [t], ); + const swapErrors: Record = useMemo( + () => ({ + [SwapErrorCode.ApiError]: { + title: t('There was an error contacting our pricing provider.'), + hint: t('Please try again later.'), + }, + [SwapErrorCode.CannotBuildTx]: { + title: t('Pricing provider did not respond with a valid transaction.'), + hint: t('Please try again later.'), + }, + [SwapErrorCode.CannotFetchAllowance]: { + title: t('There was an error fetching your spend approvals.'), + hint: t('Try swapping a different token or try again later.'), + }, + [SwapErrorCode.MissingContractMethod]: { + title: t('This token contract is missing a required method.'), + hint: t('Try swapping a different token.'), + }, + [SwapErrorCode.MissingParams]: { + title: t('Some of the required parameters are missing.'), + hint: t( + 'Our team was made aware of this issue. Feel free to contact us for further information.', + ), + }, + [SwapErrorCode.UnexpectedApiResponse]: { + title: t('Unexpected response from our pricing provider.'), + hint: t('Please try again later.'), + }, + [SwapErrorCode.UnknownSpender]: { + title: t('Unexpected response from our pricing provider.'), + hint: t('Please try again later.'), + }, + }), + [t], + ); + const commonErrors: Record = useMemo( () => ({ [CommonError.Unknown]: { @@ -122,7 +159,7 @@ export const useErrorMessage = () => { }, [CommonError.NetworkError]: { title: t('Network error'), - hint: t('Please try again'), + hint: t('Please check your connection and try again.'), }, [CommonError.NoActiveAccount]: { title: t('No account is active'), @@ -142,6 +179,12 @@ export const useErrorMessage = () => { title: t('Request timed out'), hint: t('This is taking longer than expected. Please try again later.'), }, + [CommonError.UnableToSign]: { + title: t('Unable to sign or broadcast transaction'), + }, + [CommonError.UnableToEstimateGas]: { + title: t('Unable to estimate gas'), + }, }), [t], ); @@ -197,6 +240,7 @@ export const useErrorMessage = () => { ...keystoreErrors, ...seedphraseImportError, ...rpcErrors, + ...swapErrors, }), [ fireblocksErrors, @@ -206,6 +250,7 @@ export const useErrorMessage = () => { keystoreErrors, seedphraseImportError, rpcErrors, + swapErrors, ], ); diff --git a/src/localization/locales/en/translation.json b/src/localization/locales/en/translation.json index bddca1a5b..c07d14d4c 100644 --- a/src/localization/locales/en/translation.json +++ b/src/localization/locales/en/translation.json @@ -593,6 +593,7 @@ "Or": "Or", "Or click Scan QR Code.": "Or click Scan QR Code.", "Other Wallets": "Other Wallets", + "Our team was made aware of this issue. Feel free to contact us for further information.": "Our team was made aware of this issue. Feel free to contact us for further information.", "Outgoing": "Outgoing", "Owner": "Owner", "Owners": "Owners", @@ -614,6 +615,7 @@ "Please approve or reject this action on your Ledger device. Learn more.": "Please approve or reject this action on your Ledger device. Learn more.", "Please approve the second request, too.": "Please approve the second request, too.", "Please check back later and try again.": "Please check back later and try again.", + "Please check your connection and try again.": "Please check your connection and try again.", "Please close this tab and open the Core Browser Extension to see the newly imported wallet.": "Please close this tab and open the Core Browser Extension to see the newly imported wallet.", "Please configure multi-factor authentication first.": "Please configure multi-factor authentication first.", "Please confirm the public key displayed on your Ledger device.": "Please confirm the public key displayed on your Ledger device.", @@ -637,6 +639,7 @@ "Please try again": "Please try again", "Please try again later or contact Core support.": "Please try again later or contact Core support.", "Please try again later or contact support.": "Please try again later or contact support.", + "Please try again later.": "Please try again later.", "Please try again.": "Please try again.", "Please update the Avalanche Application on your Ledger device to continue.": "Please update the Avalanche Application on your Ledger device to continue.", "Please use your FIDO device ({{ deviceName }}) to continue.": "Please use your FIDO device ({{ deviceName }}) to continue.", @@ -652,6 +655,7 @@ "Powered by": "Powered by", "Pressing yes will terminate this session. Without your recovery phrase or methods you will not be able to recover this wallet.": "Pressing yes will terminate this session. Without your recovery phrase or methods you will not be able to recover this wallet.", "Previous": "Previous", + "Pricing provider did not respond with a valid transaction.": "Pricing provider did not respond with a valid transaction.", "Primary Network": "Primary Network", "Privacy Policy": "Privacy Policy", "Private Key": "Private Key", @@ -791,6 +795,7 @@ "Signing this message can be dangerous. This signature could potentially perform any operation on your account's behalf, including granting complete control of your account and all of its assets to the requesting site. Only sign this message if you know what you're doing or completely trust the requesting site": "Signing this message can be dangerous. This signature could potentially perform any operation on your account's behalf, including granting complete control of your account and all of its assets to the requesting site. Only sign this message if you know what you're doing or completely trust the requesting site", "Skip": "Skip", "Slippage tolerance": "Slippage tolerance", + "Some of the required parameters are missing.": "Some of the required parameters are missing.", "Something Went Wrong": "Something Went Wrong", "Something bad happened please try again later!": "Something bad happened please try again later!", "Something went wrong": "Something went wrong", @@ -836,7 +841,6 @@ "Suspicious Transaction": "Suspicious Transaction", "Swap": "Swap", "Swap Approval": "Swap Approval", - "Swap Failed": "Swap Failed", "Swap pending...": "Swap pending...", "Swap transaction failed! ❌": "Swap transaction failed! ❌", "Swap transaction succeeded! 🎉": "Swap transaction succeeded! 🎉", @@ -871,6 +875,8 @@ "There are invalid fields in the form": "There are invalid fields in the form", "There is no search result.": "There is no search result.", "There was a problem with the transfer": "There was a problem with the transfer", + "There was an error contacting our pricing provider.": "There was an error contacting our pricing provider.", + "There was an error fetching your spend approvals.": "There was an error fetching your spend approvals.", "These are the addresses derived from your Keystone device": "These are the addresses derived from your Keystone device", "This Chain ID has been added already": "This Chain ID has been added already", "This Feature": "This Feature", @@ -891,6 +897,7 @@ "This password was set when you created the keystore file.": "This password was set when you created the keystore file.", "This recovery phrase appears to have already been imported.": "This recovery phrase appears to have already been imported.", "This recovery phrase is already imported.": "This recovery phrase is already imported.", + "This token contract is missing a required method.": "This token contract is missing a required method.", "This token has been flagged as malicious": "This token has been flagged as malicious", "This token has been flagged as malicious. Use caution when interacting with it.": "This token has been flagged as malicious. Use caution when interacting with it.", "This transaction is malicious do not proceed.": "This transaction is malicious do not proceed.", @@ -947,6 +954,8 @@ "Trouble Connecting": "Trouble Connecting", "Try Again": "Try Again", "Try again": "Try again", + "Try swapping a different token or try again later.": "Try swapping a different token or try again later.", + "Try swapping a different token.": "Try swapping a different token.", "Try typing the information again or go back to the account manager.": "Try typing the information again or go back to the account manager.", "Turkish": "Turkish", "Type": "Type", @@ -954,9 +963,12 @@ "USDC is routed through Circle's Cross-Chain Transfer Protocol. Bridge FAQs": "USDC is routed through Circle's Cross-Chain Transfer Protocol. Bridge FAQs", "Unable to connect. View the troubleshoot guide here": "Unable to connect. View the troubleshoot guide here", "Unable to connect?": "Unable to connect?", + "Unable to estimate gas": "Unable to estimate gas", "Unable to set TOTP configuration": "Unable to set TOTP configuration", + "Unable to sign or broadcast transaction": "Unable to sign or broadcast transaction", "Unauthorized": "Unauthorized", "Unavailable": "Unavailable", + "Unexpected response from our pricing provider.": "Unexpected response from our pricing provider.", "United States Dollar": "United States Dollar", "Unknown": "Unknown", "Unknown Site": "Unknown Site", diff --git a/src/monitoring/sentryCaptureException.ts b/src/monitoring/sentryCaptureException.ts index 3b651f285..ef07a60c7 100644 --- a/src/monitoring/sentryCaptureException.ts +++ b/src/monitoring/sentryCaptureException.ts @@ -8,6 +8,8 @@ export enum SentryExceptionTypes { EXTENSION_CONNECTION_MESSAGE = 'extensionConnectionMessage', EXTENSION_CONNECTION_EVENT = 'extensionConnectionEvent', + SWAP = 'swap', + // ledger LEDGER = 'ledger', diff --git a/src/pages/Swap/Swap.tsx b/src/pages/Swap/Swap.tsx index 2a0d2f6f4..bb511e97a 100644 --- a/src/pages/Swap/Swap.tsx +++ b/src/pages/Swap/Swap.tsx @@ -29,12 +29,17 @@ import { Button, styled, IconButton, + ToastCard, } from '@avalabs/core-k2-components'; +import sentryCaptureException, { + SentryExceptionTypes, +} from '@src/monitoring/sentryCaptureException'; import { TokenSelect } from '@src/components/common/TokenSelect'; import { useAccountsContext } from '@src/contexts/AccountsProvider'; import { isBitcoinNetwork } from '@src/background/services/network/utils/isBitcoinNetwork'; import { isUserRejectionError } from '@src/utils/errors'; import { DISALLOWED_SWAP_ASSETS } from '@src/contexts/SwapProvider/models'; +import { useErrorMessage } from '@src/hooks/useErrorMessage'; import { SwappableToken } from './models'; const ReviewOrderButtonContainer = styled('div')<{ @@ -53,6 +58,7 @@ export function Swap() { const { network } = useNetworkContext(); const { swap } = useSwapContext(); const { networkFee } = useNetworkFeeContext(); + const getTranslatedError = useErrorMessage(); const { isFunctionAvailable: isSwapAvailable, @@ -176,7 +182,23 @@ export function Swap() { } if (error && !isUserRejectionError(error)) { - toast.error(t('Swap Failed')); + console.error(error); + sentryCaptureException(error, SentryExceptionTypes.SWAP); + + const { title, hint } = getTranslatedError(error); + toast.custom( + + + {title} + + {hint && ( + + {hint} + + )} + , + { duration: 5000 }, + ); captureEncrypted('SwapFailed', { address: activeAddress, chainId: network?.chainId, diff --git a/src/utils/errors/errorCodes.ts b/src/utils/errors/errorCodes.ts index 1ad48b22a..ba8144075 100644 --- a/src/utils/errors/errorCodes.ts +++ b/src/utils/errors/errorCodes.ts @@ -3,6 +3,7 @@ import { SeedlessError } from '@src/background/services/seedless/models'; import { UnifiedBridgeError } from '@src/background/services/unifiedBridge/models'; import { SeedphraseImportError } from '@src/background/services/wallet/handlers/models'; import { VMModuleError } from '@src/background/vmModules/models'; +import { SwapErrorCode } from '@src/contexts/SwapProvider/models'; import { KeystoreError } from '@src/utils/keystore/models'; export enum CommonError { @@ -14,6 +15,8 @@ export enum CommonError { UnknownNetwork = 'unknown-network', UnknownNetworkFee = 'unknown-network-fee', RequestTimeout = 'request-timeout', + UnableToSign = 'unable-to-sign', + UnableToEstimateGas = 'unable-to-estimate-gas', } export enum RpcErrorCode { @@ -22,6 +25,7 @@ export enum RpcErrorCode { export type ErrorCode = | FireblocksErrorCode + | SwapErrorCode | CommonError | UnifiedBridgeError | SeedphraseImportError From 8b59301e95ba8c837ce3bc437ea468f65f86cd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Thu, 30 Jan 2025 22:00:58 +0100 Subject: [PATCH 2/4] chore: some more error messages --- src/contexts/SwapProvider/swap-utils.ts | 48 ++++++++++++++++++++ src/localization/locales/en/translation.json | 7 ++- src/pages/Swap/components/SwapError.tsx | 12 ----- src/pages/Swap/hooks/useSwap.tsx | 25 ++-------- 4 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/contexts/SwapProvider/swap-utils.ts b/src/contexts/SwapProvider/swap-utils.ts index 552ddd4be..498cbc4ae 100644 --- a/src/contexts/SwapProvider/swap-utils.ts +++ b/src/contexts/SwapProvider/swap-utils.ts @@ -5,15 +5,18 @@ import { RpcMethod } from '@avalabs/vm-module-types'; import { JsonRpcBatchInternal } from '@avalabs/core-wallets-sdk'; import { ethErrors } from 'eth-rpc-errors'; import ERC20 from '@openzeppelin/contracts/build/contracts/ERC20.json'; +import { t } from 'i18next'; import { CommonError, + WrappedError, isUserRejectionError, isWrappedError, wrapError, } from '@src/utils/errors'; import { resolve } from '@src/utils/promiseResolver'; import { RequestHandlerType } from '@src/background/connections/models'; +import { SwapError } from '@src/pages/Swap/hooks/useSwap'; import { PARASWAP_RETRYABLE_ERRORS, @@ -260,6 +263,51 @@ export const swapError = ( }); }; +export const paraswapErrorToSwapError = (error: WrappedError): SwapError => { + if (!error.data.originalError) { + return { + message: t('Unknown error occurred, '), + hasTryAgain: true, + }; + } + + const originalError = error.data.originalError as Error; + + switch (originalError.message) { + case 'ESTIMATED_LOSS_GREATER_THAN_MAX_IMPACT': + return { + message: t( + 'Amount too low or too big to cover. Please adjust swap values.', + ), + hasTryAgain: false, + }; + + case 'No routes found with enough liquidity': + return { + message: t('No routes found with enough liquidity.'), + hasTryAgain: false, + }; + + case 'Internal Error while computing the price': + return { + message: t('An error occurred while computing the price.'), + hasTryAgain: false, + }; + } + + if (/is too small to proceed/.test(originalError.message)) { + return { + message: t('Amount is too small to proceed.'), + hasTryAgain: false, + }; + } + + return { + message: t('Unknown error occurred, '), + hasTryAgain: true, + }; +}; + export function checkForErrorsInGetRateResult( response: ParaswapPricesResponse | TypeError, ) { diff --git a/src/localization/locales/en/translation.json b/src/localization/locales/en/translation.json index c07d14d4c..e8007a150 100644 --- a/src/localization/locales/en/translation.json +++ b/src/localization/locales/en/translation.json @@ -73,8 +73,11 @@ "Almost done!": "Almost done!", "Amount": "Amount", "Amount is too low": "Amount is too low", + "Amount is too small to proceed.": "Amount is too small to proceed.", "Amount required": "Amount required", "Amount too low -- minimum is {{minimum}}": "Amount too low -- minimum is {{minimum}}", + "Amount too low or too big to cover. Please adjust swap values.": "Amount too low or too big to cover. Please adjust swap values.", + "An error occurred while computing the price.": "An error occurred while computing the price.", "An error occurred, please try again later": "An error occurred, please try again later", "An error occurred. Please reach out to Core Support": "An error occurred. Please reach out to Core Support", "An unexpected error occurred. Please try again.": "An unexpected error occurred. Please try again.", @@ -326,7 +329,6 @@ "Error:": "Error:", "Estimated": "Estimated", "Estimated gas units needed to complete the transaction. Includes a small buffer. Not editable for this transaction.": "Estimated gas units needed to complete the transaction. Includes a small buffer. Not editable for this transaction.", - "Estimated loss greater than impact. Try lowering the amount.": "Estimated loss greater than impact. Try lowering the amount.", "Ethereum Address": "Ethereum Address", "Euro": "Euro", "Explore Ecosystem": "Explore Ecosystem", @@ -566,6 +568,7 @@ "No assets": "No assets", "No custom headers are configured.": "No custom headers are configured.", "No custom headers are configured. ": "No custom headers are configured. ", + "No routes found with enough liquidity.": "No routes found with enough liquidity.", "No search results found": "No search results found", "Node ID": "Node ID", "Normal": "Normal", @@ -801,7 +804,6 @@ "Something went wrong": "Something went wrong", "Something went wrong while attempting to show the info for this transaction.": "Something went wrong while attempting to show the info for this transaction.", "Something went wrong while opening the approval window for this transaction.": "Something went wrong while opening the approval window for this transaction.", - "Something went wrong, ": "Something went wrong, ", "Something went wrong.": "Something went wrong.", "Something went wrong. Please try again.": "Something went wrong. Please try again.", "Sorry": "Sorry", @@ -975,6 +977,7 @@ "Unknown Symbol": "Unknown Symbol", "Unknown Transaction": "Unknown Transaction", "Unknown error": "Unknown error", + "Unknown error occurred, ": "Unknown error occurred, ", "Unknown network": "Unknown network", "Unknown network fee": "Unknown network fee", "Unknown sign type": "Unknown sign type", diff --git a/src/pages/Swap/components/SwapError.tsx b/src/pages/Swap/components/SwapError.tsx index f20220d5c..3de1fd220 100644 --- a/src/pages/Swap/components/SwapError.tsx +++ b/src/pages/Swap/components/SwapError.tsx @@ -1,10 +1,8 @@ import BN from 'bn.js'; import { useTranslation } from 'react-i18next'; import { - InfoCircleIcon, Stack, Typography, - Tooltip, styled, useTheme, } from '@avalabs/core-k2-components'; @@ -26,8 +24,6 @@ export function SwapError({ const theme = useTheme(); const { t } = useTranslation(); - const errorTitle = swapError?.errorInfo?.message || swapError?.message || ''; - return ( @@ -53,14 +49,6 @@ export function SwapError({ > {t('try again')} - {errorTitle && ( - - - - )} )} diff --git a/src/pages/Swap/hooks/useSwap.tsx b/src/pages/Swap/hooks/useSwap.tsx index c84d5897b..e9fb2f60c 100644 --- a/src/pages/Swap/hooks/useSwap.tsx +++ b/src/pages/Swap/hooks/useSwap.tsx @@ -1,15 +1,16 @@ import { useEffect, useMemo, useState } from 'react'; import { BehaviorSubject, debounceTime } from 'rxjs'; import { OptimalRate, SwapSide } from 'paraswap-core'; +import { useTranslation } from 'react-i18next'; + +import { paraswapErrorToSwapError } from '@src/contexts/SwapProvider/swap-utils'; import { useSwapContext } from '@src/contexts/SwapProvider/SwapProvider'; + import { DestinationInput, isAPIError } from '../utils'; -import { useTranslation } from 'react-i18next'; export interface SwapError { message?: string; hasTryAgain?: boolean; - errorInfo?: string; - canSwap?: boolean; } export function useSwap() { @@ -115,23 +116,7 @@ export function useSwap() { .catch((error) => { setOptimalRate(undefined); setDestAmount(''); - if ( - error?.message === 'ESTIMATED_LOSS_GREATER_THAN_MAX_IMPACT' - ) { - setSwapError({ - message: t( - 'Estimated loss greater than impact. Try lowering the amount.', - ), - hasTryAgain: false, - errorInfo: error, - }); - } else { - setSwapError({ - message: t('Something went wrong, '), - hasTryAgain: true, - errorInfo: error, - }); - } + setSwapError(paraswapErrorToSwapError(error)); }) .finally(() => { if (!isCalculateAvaxMax) { From e60c1339a59d3a7081b128c6d69a680747e7748b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Wed, 19 Feb 2025 08:39:21 +0100 Subject: [PATCH 3/4] fix: svm-related typings issues --- .../middlewares/ExtensionRequestHandlerMiddleware.ts | 4 ++-- src/background/services/accounts/AccountsService.test.ts | 2 ++ src/background/services/balances/BalancesService.ts | 3 ++- src/background/services/history/HistoryService.test.ts | 4 ++-- src/background/services/networkFee/NetworkFeeService.ts | 4 ++-- src/background/services/secrets/SecretsService.test.ts | 3 +++ src/background/services/secrets/SecretsService.ts | 2 ++ 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/background/connections/middlewares/ExtensionRequestHandlerMiddleware.ts b/src/background/connections/middlewares/ExtensionRequestHandlerMiddleware.ts index 0c195ed30..74b124c54 100644 --- a/src/background/connections/middlewares/ExtensionRequestHandlerMiddleware.ts +++ b/src/background/connections/middlewares/ExtensionRequestHandlerMiddleware.ts @@ -7,7 +7,7 @@ import { } from '../models'; import * as Sentry from '@sentry/browser'; import { ModuleManager } from '@src/background/vmModules/ModuleManager'; -import { Module } from '@avalabs/vm-module-types'; +import { Module, Network } from '@avalabs/vm-module-types'; import { runtime } from 'webextension-polyfill'; export function ExtensionRequestHandlerMiddleware( @@ -96,7 +96,7 @@ const handleRequest = async ( params: context.request.params.request.params, context: context.request.context, }, - context.network, + context.network as Network, // TODO: Remove this cast after SVM network type appears in vm-module-types, ); return { diff --git a/src/background/services/accounts/AccountsService.test.ts b/src/background/services/accounts/AccountsService.test.ts index 5e2974d8f..e5d157caf 100644 --- a/src/background/services/accounts/AccountsService.test.ts +++ b/src/background/services/accounts/AccountsService.test.ts @@ -347,6 +347,7 @@ describe('background/services/accounts/AccountsService', () => { [NetworkVMType.PVM]: pvmAddress, [NetworkVMType.CoreEth]: coreEthAddress, [NetworkVMType.HVM]: otherEvmAddress, + [NetworkVMType.SVM]: undefined, }); await accountsService.onUnlock(); @@ -410,6 +411,7 @@ describe('background/services/accounts/AccountsService', () => { [NetworkVMType.PVM]: pvmAddress, [NetworkVMType.CoreEth]: coreEthAddress, [NetworkVMType.HVM]: otherEvmAddress, + [NetworkVMType.SVM]: undefined, }); jest .mocked(secretsService.getImportedAddresses) diff --git a/src/background/services/balances/BalancesService.ts b/src/background/services/balances/BalancesService.ts index da85bc136..914576293 100644 --- a/src/background/services/balances/BalancesService.ts +++ b/src/background/services/balances/BalancesService.ts @@ -7,6 +7,7 @@ import { SettingsService } from '../settings/SettingsService'; import { getPriceChangeValues } from './utils/getPriceChangeValues'; import * as Sentry from '@sentry/browser'; import { + Network, NetworkVMType, TokenType, TokenWithBalance, @@ -66,7 +67,7 @@ export class BalancesService { } }) .filter((address): address is string => !!address), - network, + network: network as Network, // TODO: Remove this cast after SVM network type appears in vm-module-types currency, customTokens, tokenTypes, diff --git a/src/background/services/history/HistoryService.test.ts b/src/background/services/history/HistoryService.test.ts index a9aff4cb8..5249b81a3 100644 --- a/src/background/services/history/HistoryService.test.ts +++ b/src/background/services/history/HistoryService.test.ts @@ -1,7 +1,7 @@ -import { Network, NetworkToken, NetworkVMType } from '@avalabs/core-chains-sdk'; +import { Network, NetworkToken } from '@avalabs/core-chains-sdk'; import { HistoryService } from './HistoryService'; import { TxHistoryItem } from './models'; -import { TokenType } from '@avalabs/vm-module-types'; +import { NetworkVMType, TokenType } from '@avalabs/vm-module-types'; import { TransactionType } from '@avalabs/vm-module-types'; import { ETHEREUM_ADDRESS } from '@src/utils/bridgeTransactionUtils'; import { BridgeType } from '@avalabs/bridge-unified'; diff --git a/src/background/services/networkFee/NetworkFeeService.ts b/src/background/services/networkFee/NetworkFeeService.ts index 4fab2727d..42435a96f 100644 --- a/src/background/services/networkFee/NetworkFeeService.ts +++ b/src/background/services/networkFee/NetworkFeeService.ts @@ -1,4 +1,4 @@ -import { NetworkVMType } from '@avalabs/core-chains-sdk'; +import { Network, NetworkVMType } from '@avalabs/vm-module-types'; import { JsonRpcBatchInternal } from '@avalabs/core-wallets-sdk'; import { getProviderForNetwork } from '@src/utils/network/getProviderForNetwork'; import { singleton } from 'tsyringe'; @@ -12,7 +12,7 @@ export class NetworkFeeService { async getNetworkFee(network: NetworkWithCaipId): Promise { const module = await this.moduleManager.loadModuleByNetwork(network); - const fees = await module.getNetworkFee(network); + const fees = await module.getNetworkFee(network as Network); // TODO: Remove this cast after SVM network type appears in vm-module-types return { ...fees, displayDecimals: fees.displayDecimals ?? 0, diff --git a/src/background/services/secrets/SecretsService.test.ts b/src/background/services/secrets/SecretsService.test.ts index e65200de7..47985d6f3 100644 --- a/src/background/services/secrets/SecretsService.test.ts +++ b/src/background/services/secrets/SecretsService.test.ts @@ -1462,6 +1462,7 @@ describe('src/background/services/secrets/SecretsService.ts', () => { ).resolves.toStrictEqual({ ...addressesMock('0x1', '0x2'), HVM: '0xhvm', + SVM: undefined, }); }); it('returns the addresses for xpub', async () => { @@ -1473,6 +1474,7 @@ describe('src/background/services/secrets/SecretsService.ts', () => { ).resolves.toStrictEqual({ ...addressesMock('0x1', '0x2'), HVM: undefined, + SVM: undefined, }); expect(Avalanche.getAddressPublicKeyFromXpub).toBeCalledWith('xpubXP', 0); expect(getAddressFromXPub).toHaveBeenCalledWith('xpub', 0); @@ -1508,6 +1510,7 @@ describe('src/background/services/secrets/SecretsService.ts', () => { ).resolves.toStrictEqual({ ...addressesMock('0x1', '0x2'), HVM: undefined, + SVM: undefined, }); expect(getEvmAddressFromPubKey).toHaveBeenCalledWith(pubKeyBuff); diff --git a/src/background/services/secrets/SecretsService.ts b/src/background/services/secrets/SecretsService.ts index b58187c34..b0f53b91e 100644 --- a/src/background/services/secrets/SecretsService.ts +++ b/src/background/services/secrets/SecretsService.ts @@ -791,6 +791,7 @@ export class SecretsService implements OnUnlock { [NetworkVMType.AVM]: xAddr, [NetworkVMType.PVM]: pAddr, [NetworkVMType.CoreEth]: cAddr, + [NetworkVMType.SVM]: undefined, [NetworkVMType.HVM]: secrets.secretType === SecretType.Mnemonic ? getAddressForHvm( @@ -836,6 +837,7 @@ export class SecretsService implements OnUnlock { [NetworkVMType.PVM]: addrP, [NetworkVMType.CoreEth]: providerXP.getAddress(pubKeyBuffer, 'C'), [NetworkVMType.HVM]: undefined, + [NetworkVMType.SVM]: undefined, }; } From c48c75d030edd4fa6a0e60f9261bad6bd634cfe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Wed, 19 Feb 2025 09:38:52 +0100 Subject: [PATCH 4/4] chore: update vm modules --- package.json | 10 +++++----- yarn.lock | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 8a7d1ea6a..ddabfe82f 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "sentry": "node sentryscript.js" }, "dependencies": { - "@avalabs/avalanche-module": "1.4.2", + "@avalabs/avalanche-module": "1.4.3", "@avalabs/avalanchejs": "4.2.0-alpha.1", - "@avalabs/bitcoin-module": "1.4.2", + "@avalabs/bitcoin-module": "1.4.3", "@avalabs/bridge-unified": "4.0.1", "@avalabs/core-bridge-sdk": "3.1.0-alpha.37", "@avalabs/core-chains-sdk": "3.1.0-alpha.37", @@ -37,12 +37,12 @@ "@avalabs/core-token-prices-sdk": "3.1.0-alpha.37", "@avalabs/core-utils-sdk": "3.1.0-alpha.37", "@avalabs/core-wallets-sdk": "3.1.0-alpha.37", - "@avalabs/evm-module": "1.4.2", + "@avalabs/evm-module": "1.4.3", "@avalabs/glacier-sdk": "3.1.0-alpha.37", "@avalabs/hw-app-avalanche": "0.14.1", - "@avalabs/hvm-module": "1.4.2", + "@avalabs/hvm-module": "1.4.3", "@avalabs/types": "3.1.0-alpha.37", - "@avalabs/vm-module-types": "1.4.2", + "@avalabs/vm-module-types": "1.4.3", "@blockaid/client": "0.10.0", "@coinbase/cbpay-js": "1.6.0", "@cubist-labs/cubesigner-sdk": "0.3.28", diff --git a/yarn.lock b/yarn.lock index 77ac89647..b3f2fcfac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,10 +34,10 @@ resolved "https://registry.yarnpkg.com/@apocentre/alias-sampling/-/alias-sampling-0.5.3.tgz#897ff181b48ad7b2bcb4ecf29400214888244f08" integrity sha512-7UDWIIF9hIeJqfKXkNIzkVandlwLf1FWTSdrb9iXvOP8oF544JRXQjCbiTmCv2c9n44n/FIWtehhBfNuAx2CZA== -"@avalabs/avalanche-module@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@avalabs/avalanche-module/-/avalanche-module-1.4.2.tgz#2b41cbafbe08f7be38b249d80f7710888e5ba53c" - integrity sha512-/yTWVSTa7qMjP13m6AhfQNyGWebPX7Kp8DJke8SjI5414YVqxgraaS8OmUabWk1CPMVvjiBOgUclg3qVOkOk4Q== +"@avalabs/avalanche-module@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@avalabs/avalanche-module/-/avalanche-module-1.4.3.tgz#87bc8346c0934880a76b6c88c6bc831bdd5d4e64" + integrity sha512-YNmxD7FDs629a3k1ctD08FdT/OzWLPDUQbIrrJV0vgpJW0favRtqQpnZcaB+jf0hka7Gkp4sNYvda14aV8Ykcw== dependencies: "@avalabs/avalanchejs" "4.2.0-alpha.1" "@avalabs/core-chains-sdk" "3.1.0-alpha.34" @@ -47,7 +47,7 @@ "@avalabs/core-wallets-sdk" "3.1.0-alpha.34" "@avalabs/glacier-sdk" "3.1.0-alpha.34" "@avalabs/types" "3.1.0-alpha.34" - "@avalabs/vm-module-types" "1.4.2" + "@avalabs/vm-module-types" "1.4.3" "@metamask/rpc-errors" "6.3.0" big.js "6.2.1" bn.js "5.2.1" @@ -64,15 +64,15 @@ "@scure/base" "1.1.5" micro-eth-signer "0.7.2" -"@avalabs/bitcoin-module@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@avalabs/bitcoin-module/-/bitcoin-module-1.4.2.tgz#6abe4a0ec514002c019aa114b22606462f75e909" - integrity sha512-m7dQvEFglo5Dz1qoUG8tOi2MU6X2P0YhrVwqFCM9YIqTHpdrN5RN9FdLmpBz+GQ7F+CCUNqEOqgBgegjDdOAZA== +"@avalabs/bitcoin-module@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@avalabs/bitcoin-module/-/bitcoin-module-1.4.3.tgz#5ce1a103d3e0b85313a637cf452580e41b0e0ece" + integrity sha512-7gzwIeXc+cay67SwqDBUHOkV3qr6FGV9r662eyFxpFV16kSHB0nP+kKe4kV1nX5R23QZLI0IDkb9XpjiIwtn/A== dependencies: "@avalabs/core-coingecko-sdk" "3.1.0-alpha.34" "@avalabs/core-utils-sdk" "3.1.0-alpha.34" "@avalabs/core-wallets-sdk" "3.1.0-alpha.34" - "@avalabs/vm-module-types" "1.4.2" + "@avalabs/vm-module-types" "1.4.3" "@metamask/rpc-errors" "6.3.0" big.js "6.2.1" bitcoinjs-lib "5.2.0" @@ -254,10 +254,10 @@ ledger-bitcoin "0.2.3" xss "1.0.14" -"@avalabs/evm-module@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@avalabs/evm-module/-/evm-module-1.4.2.tgz#a1d24abba9e4dead85d2f4f31f2e3377449875ab" - integrity sha512-JYD7fIxi8tlM+1h8rQDZLRwQor683g1DBVGHbkwtlpD8GElA+usLhgQI50RZ/XKXTSXmBXx+1XVvuqNDnlVe0Q== +"@avalabs/evm-module@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@avalabs/evm-module/-/evm-module-1.4.3.tgz#14537abf565c822d3cfd0ffa0c4af33245d95fc5" + integrity sha512-Di6uc88Cj5M6wLYu5XVat92khNNoDpz9hBUocJxrl10Jq0MM84caJvgPAE8+4RSEzqqDHQ9U3wFnUzLeYKhhhw== dependencies: "@avalabs/core-chains-sdk" "3.1.0-alpha.34" "@avalabs/core-coingecko-sdk" "3.1.0-alpha.34" @@ -266,7 +266,7 @@ "@avalabs/core-wallets-sdk" "3.1.0-alpha.34" "@avalabs/glacier-sdk" "3.1.0-alpha.34" "@avalabs/types" "3.1.0-alpha.34" - "@avalabs/vm-module-types" "1.4.2" + "@avalabs/vm-module-types" "1.4.3" "@blockaid/client" "0.11.0" "@metamask/rpc-errors" "6.3.0" "@openzeppelin/contracts" "4.9.6" @@ -285,13 +285,13 @@ resolved "https://registry.yarnpkg.com/@avalabs/glacier-sdk/-/glacier-sdk-3.1.0-alpha.37.tgz#c1f4e3e92f4acb2eaf0b62e5ae40fe213decc86e" integrity sha512-6LeLImgFenVOU3cdK5E07oXbSpRKIlfMz2knLaKJVc51QHvvZOx+S+Q4kX6yi8NZttQYO406f0tPG0bD3Bn/tg== -"@avalabs/hvm-module@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@avalabs/hvm-module/-/hvm-module-1.4.2.tgz#cf19020df97c623e7d7fce2ff8bcf10fe077ff7e" - integrity sha512-uzSq3kreu1ANQNDXAK3pKX75gZmIJjGwCX35yTtVWmnNnNYm03OVMJrN03zM9IpL1TdaL2ArA8pOnnorWp19tA== +"@avalabs/hvm-module@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@avalabs/hvm-module/-/hvm-module-1.4.3.tgz#3fc15f0f7c34c2191bcbeb24468259661c7e64ba" + integrity sha512-d1gHAlr6o8bxsfISopuH65mSzOwpeKmYhXJpaEW+g8FdK5vq92qBkzm4sd2Tkgp+AJrzfIlBBA49PlV0UWlWIg== dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.34" - "@avalabs/vm-module-types" "1.4.2" + "@avalabs/vm-module-types" "1.4.3" "@metamask/rpc-errors" "6.3.0" "@noble/hashes" "1.5.0" hypersdk-client "0.4.16" @@ -318,10 +318,10 @@ resolved "https://registry.yarnpkg.com/@avalabs/types/-/types-3.1.0-alpha.37.tgz#2b92c7ef7e6a59c1314a541c08dbe5a812ea56e5" integrity sha512-0xbjZyNb6xTLVAaaLoalJbbDc6eFXOQphs5ZOvljAKgjtS4tc7DlrF466zMac/aMLG1YPaNfNbzJQKB1GrSsEQ== -"@avalabs/vm-module-types@1.4.2": - version "1.4.2" - resolved "https://registry.yarnpkg.com/@avalabs/vm-module-types/-/vm-module-types-1.4.2.tgz#e9cadcaf4c873d686e0916916f3dacdfc154a59f" - integrity sha512-n2xdPYgBcLbvMN/QsBLVImXhMsw2PsB5/7jWQ1wkTVBhVgqUfnke2sn0yaG/8ebT8wgEZTVWgsS3PSVccutTkw== +"@avalabs/vm-module-types@1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@avalabs/vm-module-types/-/vm-module-types-1.4.3.tgz#af3b4f9576be022774e7faba3c4a3b4add02bcc8" + integrity sha512-C42Ld9vYjXsO3+O9lmWvqVPodjRpsU3FBtMBvaD5uuW2Un5OvRnyGXrEW+9wnVSRzMtpsG9Sko6K1tzklMgoGQ== dependencies: "@avalabs/core-wallets-sdk" "3.1.0-alpha.34" "@avalabs/glacier-sdk" "3.1.0-alpha.34"