Skip to content
Open
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/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Render the confirmation "Estimated changes" section with the shared `EstimatedChanges` component from `@metamask/snap-networks-utils` ([#396](https://github.com/MetaMask/internal-snaps/pull/396))
- Estimated changes from the previous scan now stay visible while a confirmation re-scans, instead of showing a loading skeleton
- `listAccountAssets` and `getAccountBalances` now fetch live assets and balances from the chain instead of returning persisted state. ([#388](https://github.com/MetaMask/internal-snaps/pull/388))

### Fixed

- Show "Estimated changes are not available" instead of "No estimated changes" when the transaction scan returns an error result, such as for a malformed transaction ([#396](https://github.com/MetaMask/internal-snaps/pull/396))
- Report the MetaMask origin as lowercase `metamask` instead of `MetaMask` for MetaMask-initiated operations, so the origin matches the value used by the other non-EVM snaps and granted to the keyring methods, and so transaction scan requests are attributed to `https://metamask.io`. The confirmation UI keeps displaying `MetaMask`. ([#392](https://github.com/MetaMask/internal-snaps/pull/392))
- Show "Estimated changes are not available" instead of "No estimated changes" when the transaction scan returns an error result, such as for a malformed transaction ([#396](https://github.com/MetaMask/internal-snaps/pull/396))

## [4.0.0]

Expand Down
8 changes: 4 additions & 4 deletions packages/tron-wallet-snap/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 72.49,
functions: 79.91,
lines: 85.79,
statements: 85.79,
branches: 72.95,
functions: 81.35,
lines: 86.34,
statements: 86.34,
},
},
};
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": "1IlkNeNaW28YQoE0wK3OdDJctxoEBDqK6cxLQ0GyifY=",
"shasum": "JwSeAIv5PbUj0SDJ6LqTDU7fsVM6YGRA2wrVKuN/OzE=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
111 changes: 105 additions & 6 deletions packages/tron-wallet-snap/src/handlers/keyring/keyring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
} from '@metamask/snaps-sdk';

import type { SnapClient } from '../../clients/snap/SnapClient';
import { Network } from '../../constants';
import { KnownCaip19Id, Network } from '../../constants';
import type { AssetEntity } from '../../entities/assets';
import type { AccountsService } from '../../services/accounts/AccountsService';
import type { AssetsService } from '../../services/assets/AssetsService';
import type { ConfirmationHandler } from '../../services/confirmation/ConfirmationHandler';
Expand Down Expand Up @@ -65,6 +66,23 @@ describe('KeyringHandler', () => {
let mockWalletService: jest.Mocked<WalletService>;
let mockConfirmationHandler: jest.Mocked<ConfirmationHandler>;

const mockLiveAsset: AssetEntity = {
assetType: KnownCaip19Id.TrxMainnet,
keyringAccountId: mockAccount.id,
network: Network.Mainnet,
symbol: 'TRX',
decimals: 6,
rawAmount: '1000000',
uiAmount: '1',
iconUrl: '',
};

const mockShastaAsset: AssetEntity = {
...mockLiveAsset,
assetType: KnownCaip19Id.TrxShasta,
network: Network.Shasta,
};

beforeEach(() => {
mockSnapClient = {
scheduleBackgroundEvent: jest.fn().mockResolvedValue(undefined),
Expand All @@ -84,7 +102,9 @@ describe('KeyringHandler', () => {
}),
} as unknown as jest.Mocked<AccountsService>;
mockAssetsService = {
getAccountAssets: jest.fn().mockResolvedValue([]),
fetchAccountAssets: jest
.fn()
.mockResolvedValue([mockLiveAsset, mockShastaAsset]),
} as unknown as jest.Mocked<AssetsService>;
mockTransactionsService = {
checkAddressActivity: jest.fn(),
Expand Down Expand Up @@ -670,15 +690,28 @@ describe('KeyringHandler', () => {
});

describe('getAccountAssets', () => {
it('returns asset types for an account', async () => {
it('fetches live assets for all account scopes and returns their asset types', async () => {
const result = await keyringHandler.getAccountAssets(mockAccount.id);

expect(result).toStrictEqual([]);
expect(mockAssetsService.getAccountAssets).toHaveBeenCalledWith(
mockAccount.id,
expect(result).toStrictEqual([
KnownCaip19Id.TrxMainnet,
KnownCaip19Id.TrxShasta,
]);
expect(mockAssetsService.fetchAccountAssets).toHaveBeenCalledWith(
mockAccount,
);
});

it('propagates fetch failures', async () => {
mockAssetsService.fetchAccountAssets.mockRejectedValue(
new Error('network error'),
);

await expect(
keyringHandler.getAccountAssets(mockAccount.id),
).rejects.toThrow('network error');
});

it('throws when the account is not found', async () => {
mockAccountsService.findById.mockResolvedValue(null);

Expand All @@ -688,6 +721,72 @@ describe('KeyringHandler', () => {
});
});

describe('getAccountBalances', () => {
it('fetches live balances for the account and returns the balance record', async () => {
const result = await keyringHandler.getAccountBalances(mockAccount.id, [
KnownCaip19Id.TrxMainnet,
]);

expect(result).toStrictEqual({
[KnownCaip19Id.TrxMainnet]: {
amount: '1',
unit: 'TRX',
},
});
expect(mockAssetsService.fetchAccountAssets).toHaveBeenCalledWith(
mockAccount,
);
});

it('excludes token assets with zero balance', async () => {
const zeroBalanceToken: AssetEntity = {
...mockLiveAsset,
assetType: KnownCaip19Id.UsdtMainnet,
rawAmount: '0',
uiAmount: '0',
};

mockAssetsService.fetchAccountAssets.mockResolvedValue([
mockLiveAsset,
zeroBalanceToken,
]);

const result = await keyringHandler.getAccountBalances(mockAccount.id, [
KnownCaip19Id.TrxMainnet,
KnownCaip19Id.UsdtMainnet,
]);

expect(result).toStrictEqual({
[KnownCaip19Id.TrxMainnet]: {
amount: '1',
unit: 'TRX',
},
});
});

it('propagates fetch failures', async () => {
mockAssetsService.fetchAccountAssets.mockRejectedValue(
new Error('network error'),
);

await expect(
keyringHandler.getAccountBalances(mockAccount.id, [
KnownCaip19Id.TrxMainnet,
]),
).rejects.toThrow('network error');
});

it('throws when the account is not found', async () => {
mockAccountsService.findById.mockResolvedValue(null);

await expect(
keyringHandler.getAccountBalances(mockAccount.id, [
KnownCaip19Id.TrxMainnet,
]),
).rejects.toThrow('not found');
});
});

describe('getAccountTransactions', () => {
it('returns paginated transactions for an account', async () => {
const result = await keyringHandler.getAccountTransactions(
Expand Down
8 changes: 4 additions & 4 deletions packages/tron-wallet-snap/src/handlers/keyring/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,12 @@ export class KeyringHandler implements KeyringSnapRpc {
try {
validateRequest({ accountId }, ListAccountAssetsStruct);

await this.#getAccountOrThrow(accountId);
const account = await this.#getAccountOrThrow(accountId);

this.#logger.info('Listing account assets', { accountId });

const assetEntities =
await this.#assetsService.getAccountAssets(accountId);
await this.#assetsService.fetchAccountAssets(account);
const result = assetEntities
.filter(
(asset) =>
Expand Down Expand Up @@ -275,9 +275,9 @@ export class KeyringHandler implements KeyringSnapRpc {

this.#logger.info('Getting account balances', { accountId, assets });

await this.#getAccountOrThrow(accountId);
const account = await this.#getAccountOrThrow(accountId);

const assetsList = await this.#assetsService.getAccountAssets(accountId);
const assetsList = await this.#assetsService.fetchAccountAssets(account);

const assetsToUse = assetsList
.filter((asset) => assets.includes(asset.assetType))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ type WithAccountsServiceCallback = (payload: {
mockConfigProvider: { config: Config };
mockLogger: Logger;
mockAssetsService: jest.Mocked<
Pick<AssetsService, 'fetchAssetsAndBalancesForAccount' | 'saveMany'>
Pick<AssetsService, 'fetchAccountAssetsByScope' | 'saveMany'>
>;
mockSnapClient: jest.Mocked<
Pick<SnapClient, 'getBip32Entropy' | 'listEntropySources'>
Expand Down Expand Up @@ -268,9 +268,9 @@ async function withAccountsService(
};

const mockAssetsService: jest.Mocked<
Pick<AssetsService, 'fetchAssetsAndBalancesForAccount' | 'saveMany'>
Pick<AssetsService, 'fetchAccountAssetsByScope' | 'saveMany'>
> = {
fetchAssetsAndBalancesForAccount: jest.fn().mockResolvedValue([]),
fetchAccountAssetsByScope: jest.fn().mockResolvedValue([]),
saveMany: jest.fn().mockResolvedValue(undefined),
};

Expand Down Expand Up @@ -1053,21 +1053,21 @@ describe('AccountsService', () => {
...MOCK_CONFIG,
activeNetworks: [Network.Mainnet, Network.Shasta],
};
mockAssetsService.fetchAssetsAndBalancesForAccount.mockResolvedValue(
mockAssetsService.fetchAccountAssetsByScope.mockResolvedValue(
mockAssets,
);

await accountsService.synchronize([account]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledTimes(2);
expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
).toHaveBeenCalledWith(Network.Mainnet, account);
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledWith(account, Network.Mainnet);
expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
).toHaveBeenCalledWith(Network.Shasta, account);
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledWith(account, Network.Shasta);
expect(mockAssetsService.saveMany).toHaveBeenCalledWith(
expect.arrayContaining(mockAssets),
);
Expand Down Expand Up @@ -1154,7 +1154,7 @@ describe('AccountsService', () => {
...MOCK_CONFIG,
activeNetworks: [Network.Mainnet],
};
mockAssetsService.fetchAssetsAndBalancesForAccount
mockAssetsService.fetchAccountAssetsByScope
.mockRejectedValueOnce(new Error('grpc unavailable'))
.mockResolvedValueOnce(healthyAssets);

Expand Down Expand Up @@ -1199,7 +1199,7 @@ describe('AccountsService', () => {
throw new Error('toString boom');
},
};
mockAssetsService.fetchAssetsAndBalancesForAccount.mockRejectedValue(
mockAssetsService.fetchAccountAssetsByScope.mockRejectedValue(
hostileReason,
);

Expand Down Expand Up @@ -1238,7 +1238,7 @@ describe('AccountsService', () => {
await accountsService.synchronize([account]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
mockAssetsService.fetchAccountAssetsByScope,
).not.toHaveBeenCalled();
expect(mockAssetsService.saveMany).toHaveBeenCalledWith([]);
},
Expand Down Expand Up @@ -1417,8 +1417,8 @@ describe('AccountsService', () => {
await accountsService.synchronize([account]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
).toHaveBeenCalledWith(Network.Mainnet, account);
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledWith(account, Network.Mainnet);
expect(
mockTransactionsService.fetchNewTransactionsForAccount,
).toHaveBeenCalledWith(Network.Mainnet, account);
Expand Down Expand Up @@ -1463,7 +1463,7 @@ describe('AccountsService', () => {
]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledTimes(1);
expect(
mockTransactionsService.fetchNewTransactionsForAccount,
Expand All @@ -1488,7 +1488,7 @@ describe('AccountsService', () => {
await accountsService.synchronize([account]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledTimes(2);
},
);
Expand All @@ -1511,14 +1511,14 @@ describe('AccountsService', () => {
]);

expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledTimes(2);
expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
).toHaveBeenCalledWith(Network.Mainnet, accountA);
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledWith(accountA, Network.Mainnet);
expect(
mockAssetsService.fetchAssetsAndBalancesForAccount,
).toHaveBeenCalledWith(Network.Mainnet, accountB);
mockAssetsService.fetchAccountAssetsByScope,
).toHaveBeenCalledWith(accountB, Network.Mainnet);
},
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,7 @@ export class AccountsService {

const assetResponses = await Promise.allSettled(
combinations.map(async ({ account, scope }) => {
return this.#assetsService.fetchAssetsAndBalancesForAccount(
scope,
account,
);
return this.#assetsService.fetchAccountAssetsByScope(account, scope);
}),
);

Expand Down
Loading
Loading