diff --git a/package.json b/package.json index 4d440f48a..ddabfe82f 100644 --- a/package.json +++ b/package.json @@ -23,26 +23,26 @@ "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.34", - "@avalabs/core-chains-sdk": "3.1.0-alpha.34", - "@avalabs/core-coingecko-sdk": "3.1.0-alpha.34", - "@avalabs/core-covalent-sdk": "3.1.0-alpha.34", - "@avalabs/core-etherscan-sdk": "3.1.0-alpha.34", + "@avalabs/core-bridge-sdk": "3.1.0-alpha.37", + "@avalabs/core-chains-sdk": "3.1.0-alpha.37", + "@avalabs/core-coingecko-sdk": "3.1.0-alpha.37", + "@avalabs/core-covalent-sdk": "3.1.0-alpha.37", + "@avalabs/core-etherscan-sdk": "3.1.0-alpha.37", "@avalabs/core-k2-components": "4.18.0-alpha.53", - "@avalabs/core-snowtrace-sdk": "3.1.0-alpha.34", - "@avalabs/core-token-prices-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/evm-module": "1.4.2", - "@avalabs/glacier-sdk": "3.1.0-alpha.34", + "@avalabs/core-snowtrace-sdk": "3.1.0-alpha.37", + "@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.3", + "@avalabs/glacier-sdk": "3.1.0-alpha.37", "@avalabs/hw-app-avalanche": "0.14.1", - "@avalabs/hvm-module": "1.4.2", - "@avalabs/types": "3.1.0-alpha.34", - "@avalabs/vm-module-types": "1.4.2", + "@avalabs/hvm-module": "1.4.3", + "@avalabs/types": "3.1.0-alpha.37", + "@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", @@ -274,5 +274,6 @@ "@avalabs/avalanche-module>@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>@metamask/sdk-communication-layer>utf-8-validate": false, "@avalabs/avalanche-module>@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>eciesjs>secp256k1": false } - } + }, + "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" } 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, }; } diff --git a/src/contexts/SwapProvider/SwapProvider.test.tsx b/src/contexts/SwapProvider/SwapProvider.test.tsx index 492e7c356..ad7f407f5 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'; @@ -132,6 +139,10 @@ describe('contexts/SwapProvider', () => { jest.resetAllMocks(); jest.useRealTimers(); + jest + .spyOn(swapUtils, 'getParaswapSpender') + .mockResolvedValue('0xParaswapContractAddress'); + jest.spyOn(global, 'fetch').mockResolvedValue({ json: async () => ({}), ok: true, @@ -210,7 +221,7 @@ describe('contexts/SwapProvider', () => { const { getRate } = getSwapProvider(); await expect(getRate(buildGetRateParams())).rejects.toThrow( - 'Unsupported network', + swapUtils.swapError(CommonError.UnknownNetwork), ); }, ); @@ -227,7 +238,7 @@ describe('contexts/SwapProvider', () => { const { getRate } = getSwapProvider(); await expect(getRate(buildGetRateParams())).rejects.toThrow( - 'Account address missing', + swapUtils.swapError(CommonError.NoActiveAccount), ); }); }); @@ -318,7 +329,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); @@ -389,7 +400,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 ce28a6e47..349c21afc 100644 --- a/src/contexts/SwapProvider/SwapProvider.tsx +++ b/src/contexts/SwapProvider/SwapProvider.tsx @@ -1,4 +1,4 @@ -import { createContext, useCallback, useContext, useMemo, useRef } from 'react'; +import { createContext, useCallback, useContext, useMemo } from 'react'; import type { JsonRpcBatchInternal } from '@avalabs/core-wallets-sdk'; import { TransactionParams } from '@avalabs/evm-module'; import { resolve } from '@avalabs/core-utils-sdk'; @@ -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'; import { toast } from '@avalabs/core-k2-components'; @@ -65,7 +71,6 @@ export function SwapContextProvider({ children }: { children: any }) { forceShowTokensWithoutBalances: true, disallowedAssets: DISALLOWED_SWAP_ASSETS, }); - const pendingToastIdRef = useRef(''); const paraswap = useMemo( () => new ParaSwap(ChainId.AVALANCHE_MAINNET_ID, undefined, new Web3()), @@ -95,10 +100,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)) { @@ -141,21 +146,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, @@ -204,7 +194,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( @@ -213,9 +204,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; @@ -258,6 +255,15 @@ export function SwapContextProvider({ children }: { children: any }) { srcDecimals: number; destDecimals: number; }) => { + const toastId = toast.custom( + toast.remove(toastId)}> + {t('Swap pending...')} + , + { + duration: Infinity, + }, + ); + provider.waitForTransaction(txHash).then(async (tx) => { const isSuccessful = tx && tx.status === 1; @@ -280,14 +286,14 @@ export function SwapContextProvider({ children }: { children: any }) { ? t('Swap transaction succeeded! 🎉') : t('Swap transaction failed! ❌'); - toast.remove(pendingToastIdRef.current); + toast.remove(toastId); if (isSuccessful) { - toast.success(notificationText); + toast.success(notificationText, { duration: 5000 }); } if (!isSuccessful) { - toast.error(notificationText); + toast.error(notificationText, { duration: 5000 }); } browser.notifications.create({ @@ -341,12 +347,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], ); /** @@ -452,8 +458,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]; @@ -465,24 +473,15 @@ 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; } - const toastId = toast.custom( - toast.remove(toastId)}> - {t('Swap pending...')} - , - - { - duration: Infinity, - }, - ); - pendingToastIdRef.current = toastId; - notifyOnSwapResult({ provider: avaxProviderC, txHash: swapTxHash, @@ -503,7 +502,6 @@ export function SwapContextProvider({ children }: { children: any }) { avaxProviderC, getSwapTxProps, buildTx, - t, notifyOnSwapResult, request, ], @@ -586,7 +584,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( @@ -596,19 +594,10 @@ export function SwapContextProvider({ children }: { children: any }) { }), ); - const toastId = toast.custom( - toast.remove(toastId)}> - {t('Swap pending...')} - , - - { - duration: Infinity, - }, - ); - pendingToastIdRef.current = toastId; - - if (signError || !swapTxHash) { - return throwError(signError); + if (isUserRejectionError(signError)) { + throw signError; + } else if (signError || !swapTxHash) { + throw swapError(CommonError.UnableToSign, signError); } notifyOnSwapResult({ @@ -630,7 +619,6 @@ export function SwapContextProvider({ children }: { children: any }) { avaxProviderC, getSwapTxProps, request, - t, notifyOnSwapResult, buildTx, ], @@ -638,6 +626,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..498cbc4ae 100644 --- a/src/contexts/SwapProvider/swap-utils.ts +++ b/src/contexts/SwapProvider/swap-utils.ts @@ -3,14 +3,25 @@ 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 { 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, ParaswapPricesResponse, + SwapErrorCode, SwapParams, hasParaswapError, } from './models'; @@ -30,35 +41,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 +138,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 +163,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 +174,7 @@ export async function hasEnoughAllowance({ ); if (allowanceError) { - throw new Error(`Allowance Fetching Error: ${allowanceError}`); + throw swapError(SwapErrorCode.CannotFetchAllowance, allowanceError); } return allowance >= requiredAmount; @@ -186,17 +222,90 @@ 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 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( @@ -220,9 +329,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 +347,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 f158972ce..a3dcf5c00 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.", @@ -328,7 +331,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", @@ -572,6 +574,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", @@ -599,6 +602,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", @@ -620,6 +624,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.", @@ -642,6 +647,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.", @@ -657,6 +663,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", @@ -797,12 +804,12 @@ "Skip": "Skip", "Slippage tolerance": "Slippage tolerance", "Slow": "Slow", + "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", "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", @@ -842,7 +849,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! 🎉", @@ -877,6 +883,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", @@ -897,6 +905,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.", @@ -953,6 +962,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", @@ -960,15 +971,19 @@ "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", "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/monitoring/sentryCaptureException.ts b/src/monitoring/sentryCaptureException.ts index c79e34e22..784c19fb8 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 e377bf98e..a9b89cbe8 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/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) { 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 diff --git a/yarn.lock b/yarn.lock index d045855b2..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" @@ -93,14 +93,14 @@ viem "2.11.1" zod "3.23.8" -"@avalabs/core-bridge-sdk@3.1.0-alpha.34": - version "3.1.0-alpha.34" - resolved "https://registry.yarnpkg.com/@avalabs/core-bridge-sdk/-/core-bridge-sdk-3.1.0-alpha.34.tgz#3c93f3ce155ce45ad11787c71e93f12342ebdfd9" - integrity sha512-9z2LUT96/q5TPWMwA67NCycZFwOCONSK4BEStCUx49rpxU7SNZu8AeNgqE4Vuw0SuIbx2MHF05zd+MjDH7oT4w== +"@avalabs/core-bridge-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-bridge-sdk/-/core-bridge-sdk-3.1.0-alpha.37.tgz#b33dca6a45ea7533e8ac029ce3171f29a4170e01" + integrity sha512-xfCJT5AccXRBPYuAoe9cBD/GbdWZ9U2DTTScTs++i91YWn8/FHKwG2e86UwdqfgbAOruNRybrLl2cQ/maAoVog== 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/core-coingecko-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/core-chains-sdk@3.1.0-alpha.34": version "3.1.0-alpha.34" @@ -109,6 +109,13 @@ dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.34" +"@avalabs/core-chains-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-chains-sdk/-/core-chains-sdk-3.1.0-alpha.37.tgz#954d1c2fe482317d0dd7ba98cacfd49aeac273ec" + integrity sha512-qzAIbbCO9fRddYqlsGvFf9ZwasJMjbVDqxizT2VwpGkhEnKY+tEUt+ua8wRcoHLfZjMBnN2JCeljOKzAbmScog== + dependencies: + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" + "@avalabs/core-coingecko-sdk@3.1.0-alpha.34": version "3.1.0-alpha.34" resolved "https://registry.yarnpkg.com/@avalabs/core-coingecko-sdk/-/core-coingecko-sdk-3.1.0-alpha.34.tgz#4f18f78e84dc52a5e21015124b5d81e7d4e1dbf2" @@ -116,12 +123,19 @@ dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.34" -"@avalabs/core-covalent-sdk@3.1.0-alpha.34": - version "3.1.0-alpha.34" - resolved "https://registry.yarnpkg.com/@avalabs/core-covalent-sdk/-/core-covalent-sdk-3.1.0-alpha.34.tgz#c0a95725984a422df77e2b5070393b67501df429" - integrity sha512-+LYIEG9kVnm7OGsKNec+Sq8e2fTnIQz2WkeporWPGRs79VawIl4bA/1U42BK8ABvlNRtyr0GEL7aRHikx9d9tA== +"@avalabs/core-coingecko-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-coingecko-sdk/-/core-coingecko-sdk-3.1.0-alpha.37.tgz#c51eb66ce42dd1faa6a333824590966fdf496e6c" + integrity sha512-66k6PXwQl0POBdvbLl9K62Xehk3O1UNCmBgy7JbrkUagB2RXbnHZROb8kzv83Mmzf/OXEZG2nL6xO6AmS7z6jQ== dependencies: - "@avalabs/core-utils-sdk" "3.1.0-alpha.34" + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" + +"@avalabs/core-covalent-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-covalent-sdk/-/core-covalent-sdk-3.1.0-alpha.37.tgz#02b7f7608d6944f6185ecfd8f3a3aab0a00f54b3" + integrity sha512-bOL0SmMTbifesZpSYu1510LUpLbFNmQW/jsPkiC35u2ICnM8CYvqIdbd77C5neAfNAcxMISeVeHQc91+9ILswg== + dependencies: + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" "@avalabs/core-etherscan-sdk@3.1.0-alpha.34": version "3.1.0-alpha.34" @@ -130,6 +144,13 @@ dependencies: "@avalabs/core-utils-sdk" "3.1.0-alpha.34" +"@avalabs/core-etherscan-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-etherscan-sdk/-/core-etherscan-sdk-3.1.0-alpha.37.tgz#a6c7be839bd6d6def9cdb2642f7b2919e0b341d2" + integrity sha512-/w4XJItey96pEAuvotndfUHjocdUl0mCjw02lU+9HbmZPNAu8XfPlRq785+7lOXhzSU2Y02lEtDYhksh1qJMJg== + dependencies: + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" + "@avalabs/core-k2-components@4.18.0-alpha.53": version "4.18.0-alpha.53" resolved "https://registry.yarnpkg.com/@avalabs/core-k2-components/-/core-k2-components-4.18.0-alpha.53.tgz#a7a06a613b7a33706adb0817a06462f73d4324a7" @@ -153,20 +174,20 @@ react-hotkeys-hook "4.4.3" uuid "9.0.1" -"@avalabs/core-snowtrace-sdk@3.1.0-alpha.34": - version "3.1.0-alpha.34" - resolved "https://registry.yarnpkg.com/@avalabs/core-snowtrace-sdk/-/core-snowtrace-sdk-3.1.0-alpha.34.tgz#eb748199a3587ffc4d186d2f6bb4738ec62ea340" - integrity sha512-d4Ts9VQ7iQDBZyA7so+rMH0Oiv5/O4FMdxdVTUu878G+AXfL+dyLtpxFOMDvmZTl4OUlgw9SDCJKwryKm74FpA== +"@avalabs/core-snowtrace-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-snowtrace-sdk/-/core-snowtrace-sdk-3.1.0-alpha.37.tgz#51fd8bea3e13b2c788a287b879fede92af0986a1" + integrity sha512-7EKPkB3RLbV21PI5Zhy+SoxBNNsT52F260VfzfkWvLM40aj/F8RZq1k6oesDtWnZ/B0/WpGPKSGbDNVd8BO/XA== dependencies: - "@avalabs/core-utils-sdk" "3.1.0-alpha.34" + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" -"@avalabs/core-token-prices-sdk@3.1.0-alpha.34": - version "3.1.0-alpha.34" - resolved "https://registry.yarnpkg.com/@avalabs/core-token-prices-sdk/-/core-token-prices-sdk-3.1.0-alpha.34.tgz#1c27113d89e627f2469d791e60f2f02184a06d98" - integrity sha512-ctIgnONpmV7LIY9kIT2FTswWvRvZFy3Gd6aID9w0cfOMcrF5IlfK4zTDuC7V1wR02PCEALlRdItIy8MPlIx4lQ== +"@avalabs/core-token-prices-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-token-prices-sdk/-/core-token-prices-sdk-3.1.0-alpha.37.tgz#600641fca5a50542f46c2d94c63213345d91bc8f" + integrity sha512-yAFzCM7v+V5eZN8070W0299/TNvJ3/uQWHriCcLSoWOjxqcCKhPYvDJzZkuF0fFaC0yJpFNflFbak/WGTm6XFw== dependencies: - "@avalabs/core-coingecko-sdk" "3.1.0-alpha.34" - "@avalabs/core-utils-sdk" "3.1.0-alpha.34" + "@avalabs/core-coingecko-sdk" "3.1.0-alpha.37" + "@avalabs/core-utils-sdk" "3.1.0-alpha.37" "@avalabs/core-utils-sdk@3.1.0-alpha.34": version "3.1.0-alpha.34" @@ -177,6 +198,14 @@ "@hpke/core" "1.2.5" is-ipfs "6.0.2" +"@avalabs/core-utils-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-utils-sdk/-/core-utils-sdk-3.1.0-alpha.37.tgz#2b41db88ef830a727f8a9997a90eed5720da6d5d" + integrity sha512-VY4i2YVlW4A6U5VlLcus3H+8dEi1RxlTyK/RdA4zjYGCsCfOYfFFIHFfWbRJzzXxQ1auZ9cEXNnRli70sK6kPg== + dependencies: + "@hpke/core" "1.2.5" + is-ipfs "6.0.2" + "@avalabs/core-wallets-sdk@3.1.0-alpha.34": version "3.1.0-alpha.34" resolved "https://registry.yarnpkg.com/@avalabs/core-wallets-sdk/-/core-wallets-sdk-3.1.0-alpha.34.tgz#07b3b639f8eba37e8fe560aecf9ca05d8a878c30" @@ -201,10 +230,34 @@ 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/core-wallets-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + resolved "https://registry.yarnpkg.com/@avalabs/core-wallets-sdk/-/core-wallets-sdk-3.1.0-alpha.37.tgz#19267f5d629f77c9fdbca093b71f6005f432fffe" + integrity sha512-PTmwWzAVA7D0YeqqiEk5wVkzf+aNQPls2do15hSChHtYXacFVxeXZhRRjaJQhLD8FF/VL7Qr9A3xPDwVfQVbGg== + dependencies: + "@avalabs/avalanchejs" "4.2.0-alpha.1" + "@avalabs/core-chains-sdk" "3.1.0-alpha.37" + "@avalabs/glacier-sdk" "3.1.0-alpha.37" + "@avalabs/hw-app-avalanche" "0.14.1" + "@ledgerhq/hw-app-btc" "10.2.4" + "@ledgerhq/hw-app-eth" "6.36.1" + "@ledgerhq/hw-transport" "6.30.6" + "@metamask/eth-sig-util" "7.0.2" + "@openzeppelin/contracts" "4.9.6" + bip32 "2.0.6" + bip32-path "0.4.2" + bip39 "3.1.0" + bitcoinjs-lib "5.2.0" + coinselect "3.1.13" + create-hash "1.2.0" + hdkey "2.0.1" + ledger-bitcoin "0.2.3" + xss "1.0.14" + +"@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" @@ -213,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" @@ -227,13 +280,18 @@ resolved "https://registry.yarnpkg.com/@avalabs/glacier-sdk/-/glacier-sdk-3.1.0-alpha.34.tgz#bc2b20bba5b1d5d6d970e4ed9989da37a7d3247a" integrity sha512-LtMvlWuLwhlKcc82bUFQgXeLjP2QOu3zh7UAn1AgkEOM3+XbX5hZ+I8gP0RxOHk3mzAsJOGIrpEXRIznYBvZIw== -"@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/glacier-sdk@3.1.0-alpha.37": + version "3.1.0-alpha.37" + 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.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" @@ -255,10 +313,15 @@ resolved "https://registry.yarnpkg.com/@avalabs/types/-/types-3.1.0-alpha.34.tgz#f560f7e512e4a11f0a94ad2718aa7ce57367200e" integrity sha512-v2/PAO+3QMk+xoubHzE810ZbZsIGXk+05eTXZvrNQy5KQjoPS02h11/EOSMGhpuAPg3jNCMLKQrIVkmKO/NFIw== -"@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/types@3.1.0-alpha.37": + version "3.1.0-alpha.37" + 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.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"