From 61a6817f5f2cf1f231b34891ce27d15081c458f7 Mon Sep 17 00:00:00 2001 From: Ulisses Ferreira Date: Fri, 14 Aug 2026 15:28:38 +0100 Subject: [PATCH] fix(tron-wallet-snap): distinguish inactive accounts from HTTP failures on `fetchAssetsAndBalancesForAccount` --- packages/tron-wallet-snap/snap.manifest.json | 2 +- .../assets/adapters/CoreAssetsAdapter.test.ts | 66 ++++++++++++++----- .../assets/adapters/CoreAssetsAdapter.ts | 36 ++++++---- 3 files changed, 72 insertions(+), 32 deletions(-) diff --git a/packages/tron-wallet-snap/snap.manifest.json b/packages/tron-wallet-snap/snap.manifest.json index ba9219039..3e41a75cf 100644 --- a/packages/tron-wallet-snap/snap.manifest.json +++ b/packages/tron-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "FU31oTv0a8tSdqTETs0+VMPx9OX+D7WP1BX6fJr47r4=", + "shasum": "tjWVt/iPBoSqDOS1uPATvu4MiZ4mkgwuEEZlMNDAwHc=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.test.ts b/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.test.ts index 781b5ad3d..08aa13acd 100644 --- a/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.test.ts +++ b/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.test.ts @@ -5,7 +5,10 @@ import { emitSnapKeyringEvent } from '@metamask/keyring-snap-sdk'; import { AssetsProvider } from '@metamask/snap-networks-utils'; import type { AccountResources } from '../../../clients/tron-http'; -import { TrongridAccountNotFoundError } from '../../../clients/trongrid/errors'; +import { + TrongridAccountNotFoundError, + TrongridHttpError, +} from '../../../clients/trongrid/errors'; import { KnownCaip19Id, Network } from '../../../constants'; import type { AssetEntity } from '../../../entities/assets'; import { getSnapOwnedAssetIdsForScope } from '../utils/isSnapOwnedAsset'; @@ -341,8 +344,12 @@ describe('CoreAssetsAdapter', () => { }); describe('fetchAssetsAndBalancesForAccount', () => { - it('returns only snap-owned assets when account info fails', async () => { - await withCoreAssetsAdapter(async ({ adapter }) => { + it('returns zero snap-owned assets when the account is inactive', async () => { + await withCoreAssetsAdapter(async ({ adapter, mockGetAddressInfo }) => { + mockGetAddressInfo.mockRejectedValue( + new TrongridAccountNotFoundError(), + ); + const assets = await adapter.fetchAssetsAndBalancesForAccount( Network.Mainnet, mockAccount, @@ -358,26 +365,49 @@ describe('CoreAssetsAdapter', () => { }); }); - it('does not throw when account info and rewards requests reject', async () => { - await withCoreAssetsAdapter( - async ({ - adapter, - mockGetAddressInfo, - mockGetAddressStakingRewards, - }) => { - mockGetAddressInfo.mockRejectedValue( - new TrongridAccountNotFoundError(), - ); - mockGetAddressStakingRewards.mockRejectedValue( - new Error('rewards unavailable'), - ); + it('throws when account info fails with an HTTP error', async () => { + await withCoreAssetsAdapter(async ({ adapter, mockGetAddressInfo }) => { + mockGetAddressInfo.mockRejectedValue(new TrongridHttpError(500)); - const assets = await adapter.fetchAssetsAndBalancesForAccount( + await expect( + adapter.fetchAssetsAndBalancesForAccount( Network.Mainnet, mockAccount, + ), + ).rejects.toThrow(TrongridHttpError); + }); + }); + + it('throws when account resources request rejects', async () => { + await withCoreAssetsAdapter( + async ({ adapter, mockGetAddressResources }) => { + mockGetAddressResources.mockRejectedValue( + new Error('HTTP error! status: 500'), + ); + + await expect( + adapter.fetchAssetsAndBalancesForAccount( + Network.Mainnet, + mockAccount, + ), + ).rejects.toThrow('HTTP error! status: 500'); + }, + ); + }); + + it('throws when staking rewards request rejects', async () => { + await withCoreAssetsAdapter( + async ({ adapter, mockGetAddressStakingRewards }) => { + mockGetAddressStakingRewards.mockRejectedValue( + new Error('HTTP error! status: 503'), ); - expect(assets).toHaveLength(9); + await expect( + adapter.fetchAssetsAndBalancesForAccount( + Network.Mainnet, + mockAccount, + ), + ).rejects.toThrow('HTTP error! status: 503'); }, ); }); diff --git a/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.ts b/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.ts index e60b41cfe..a71070cad 100644 --- a/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.ts +++ b/packages/tron-wallet-snap/src/services/assets/adapters/CoreAssetsAdapter.ts @@ -9,12 +9,12 @@ import { emitSnapKeyringEvent } from '@metamask/keyring-snap-sdk'; import type { AssetsProvider } from '@metamask/snap-networks-utils'; import type { TronHttpClient } from '../../../clients/tron-http/TronHttpClient'; +import { TrongridAccountNotFoundError } from '../../../clients/trongrid/errors'; import type { TrongridApiClient } from '../../../clients/trongrid/TrongridApiClient'; import { Network } from '../../../constants'; import type { AssetEntity } from '../../../entities/assets'; import type { ILogger } from '../../../utils/logger'; import logger, { createPrefixedLogger } from '../../../utils/logger'; -import { buildAccountResources } from '../utils/buildAccountResources'; import { buildStakedData } from '../utils/buildStakedData'; import { extractBandwidth } from '../utils/extractBandwidth'; import { extractEnergy } from '../utils/extractEnergy'; @@ -155,8 +155,10 @@ export class CoreAssetsAdapter { }); /** - * We use `Promise.allSettled` to avoid failing the whole fetch if one of the requests fails. - * We expect `getAccountInfoByAddress` to fail for inactive accounts. + * `getAccountInfoByAddress` rejects with `TrongridAccountNotFoundError` for + * inactive accounts. We still wait for all three requests, then rethrow + * unexpected failures (HTTP errors, timeouts) so they are not mistaken for + * an inactive account. */ const [ addressInfoRequest, @@ -168,19 +170,27 @@ export class CoreAssetsAdapter { this.#getAddressStakingRewards(scope, account.address), ]); - if (addressInfoRequest.status === 'rejected') { - this.#logger.info( - 'Account info request failed, treating as inactive account', - { account, scope }, - ); + /** + * If any of the requests fail let's treat it as a panic except for the inactive account case. + */ + if ( + addressInfoRequest.status === 'rejected' && + !(addressInfoRequest.reason instanceof TrongridAccountNotFoundError) + ) { + throw addressInfoRequest.reason; + } + + if (addressResourcesRequest.status === 'rejected') { + throw addressResourcesRequest.reason; + } + + if (addressStakingRewardsRequest.status === 'rejected') { + throw addressStakingRewardsRequest.reason; } const stakedData = buildStakedData(addressInfoRequest); - const resources = buildAccountResources(addressResourcesRequest); - const stakingRewards = - addressStakingRewardsRequest.status === 'fulfilled' - ? Math.max(0, addressStakingRewardsRequest.value) - : 0; + const resources = addressResourcesRequest.value; + const stakingRewards = Math.max(0, addressStakingRewardsRequest.value); return [ ...extractStakedNativeAssets(account, scope, stakedData),