Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/tron-wallet-snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -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');
},
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand Down