Skip to content
Draft
5 changes: 5 additions & 0 deletions eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@
"count": 1
}
},
"packages/tron-wallet-snap/src/services/assets/AssetsService.ts": {
"import-x/no-extraneous-dependencies": {
"count": 1
}
},
"packages/tron-wallet-snap/src/services/assets/AssetsService.test.ts": {
"@typescript-eslint/explicit-function-return-type": {
"count": 3
Expand Down
2 changes: 1 addition & 1 deletion packages/bitcoin-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": "9IkalnyxHKA+stXZw1ye2aW15JgXKtLhwfTUujztrHs=",
"shasum": "ye8FAG8Punj6snX/zr6AwKJCidDHdim1FUHqKwjTQog=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
8 changes: 8 additions & 0 deletions packages/tron-wallet-snap/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Wire Core messenger endowment and resolve assets migration stage from remote feature flags on each account asset read (no routing yet).

### Changed

- Update `snap.manifest.json` bundle shasum ([#82](https://github.com/MetaMask/internal-snaps/pull/82))

## [2.0.0]

### Changed
Expand Down
3 changes: 3 additions & 0 deletions packages/tron-wallet-snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
},
"devDependencies": {
"@metamask/assets-controller": "^13.0.0",
"@metamask/auto-changelog": "^6.1.1",
"@metamask/key-tree": "^10.1.1",
"@metamask/keyring-api": "^23.7.0",
"@metamask/keyring-snap-sdk": "^9.2.1",
"@metamask/messenger": "^2.0.0",
"@metamask/remote-feature-flag-controller": "4.2.2",
"@metamask/snaps-cli": "^8.4.1",
"@metamask/snaps-jest": "^10.2.0",
"@metamask/snaps-sdk": "^11.2.0",
Expand Down
5 changes: 4 additions & 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": "v/z/ZIpUZySW36sgXifEyUGKQXotRHLvezPeeP2xQAk=",
"shasum": "0iQPrqMfKKglP5LdgIV3JaesSMVm2Ac5gW7XafYtI/A=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down Expand Up @@ -48,6 +48,9 @@
},
"endowment:assets": {
"scopes": ["tron:728126428"]
},
"endowment:messenger": {
"actions": ["RemoteFeatureFlagController:getState"]
}
},
"platformVersion": "11.2.0",
Expand Down
6 changes: 6 additions & 0 deletions packages/tron-wallet-snap/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getMessenger } from '@metamask/snaps-sdk';

import { InMemoryCache } from './caching/InMemoryCache';
import { StateCache } from './caching/StateCache';
import { PriceApiClient } from './clients/price-api/PriceApiClient';
Expand Down Expand Up @@ -29,6 +31,7 @@ import { TransactionScanService } from './services/transaction-scan/TransactionS
import { TransactionsRepository } from './services/transactions/TransactionsRepository';
import { TransactionsService } from './services/transactions/TransactionsService';
import { WalletService } from './services/wallet/WalletService';
import type { CoreMessenger } from './types/core-messenger';
import logger, { noOpLogger } from './utils/logger';

/**
Expand Down Expand Up @@ -82,6 +85,8 @@ const priceApiClient = new PriceApiClient(configProvider, priceCache);
// Token API client
const tokenApiClient = new TokenApiClient(configProvider);

const coreMessenger = getMessenger<CoreMessenger>();

// Security Alerts API client
const securityAlertsApiClient = new SecurityAlertsApiClient(
configProvider,
Expand All @@ -98,6 +103,7 @@ const assetsService = new AssetsService({
priceApiClient,
tokenApiClient,
snapClient,
coreMessenger,
});

const transactionsService = new TransactionsService({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import {
SNAPS_ASSETS_MIGRATION_FLAG_KEYS,
SnapsAssetsMigrationStage,
} from '@metamask/assets-controller';
import type { KeyringAccount } from '@metamask/keyring-api';
import { KeyringEvent } from '@metamask/keyring-api';
import { emitSnapKeyringEvent } from '@metamask/keyring-snap-sdk';
import type { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';

import { MOCK_EXCHANGE_RATES } from '../../clients/price-api/mocks/exchange-rates';
import type { PriceApiClient } from '../../clients/price-api/PriceApiClient';
import type { SpotPrices } from '../../clients/price-api/types';
import type { SnapClient } from '../../clients/snap/SnapClient';
Expand All @@ -12,6 +18,7 @@ import type { TrongridApiClient } from '../../clients/trongrid/TrongridApiClient
import type { Trc20Balance, TronAccount } from '../../clients/trongrid/types';
import { KnownCaip19Id, Network } from '../../constants';
import type { AssetEntity } from '../../entities/assets';
import type { CoreMessengerCaller } from '../../types/core-messenger';
import { mockLogger } from '../../utils/mockLogger';
import type { AssetsRepository } from './AssetsRepository';
import type { NativeCaipAssetType, TokenCaipAssetType } from './types';
Expand Down Expand Up @@ -51,6 +58,14 @@ jest.mock('@metamask/keyring-snap-sdk', () => ({
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { AssetsService } = require('./AssetsService');

const TRON_FLAG_KEY = SNAPS_ASSETS_MIGRATION_FLAG_KEYS.tron;

const DEFAULT_REMOTE_FEATURE_FLAG_CONTROLLER_STATE: RemoteFeatureFlagControllerState =
{
remoteFeatureFlags: {},
cacheTimestamp: 0,
};

const mockAccount: KeyringAccount = {
id: 'test-account-id',
address: 'TGJn1wnUYHJbvN88cynZbsAz2EMeZq73yx',
Expand Down Expand Up @@ -187,6 +202,8 @@ type WithAssetsServiceCallback<ReturnValue> = (payload: {
>;
mockTokenApiClient: jest.Mocked<Pick<TokenApiClient, 'getTokensMetadata'>>;
mockSnapClient: jest.Mocked<Pick<SnapClient, 'trackError'>>;
mockCoreMessenger: jest.Mocked<CoreMessengerCaller>;
setMigrationStage: (stage: SnapsAssetsMigrationStage) => void;
}) => Promise<ReturnValue> | ReturnValue;

/**
Expand Down Expand Up @@ -259,6 +276,19 @@ async function withAssetsService<ReturnValue>(
trackError: jest.fn().mockResolvedValue(undefined),
};

const mockCoreMessenger: jest.Mocked<CoreMessengerCaller> = {
call: jest
.fn()
.mockResolvedValue(DEFAULT_REMOTE_FEATURE_FLAG_CONTROLLER_STATE),
};

const setMigrationStage = (stage: SnapsAssetsMigrationStage): void => {
mockCoreMessenger.call.mockResolvedValue({
...DEFAULT_REMOTE_FEATURE_FLAG_CONTROLLER_STATE,
remoteFeatureFlags: { [TRON_FLAG_KEY]: stage },
});
};

const assetsService = new AssetsService({
logger: mockLogger,
assetsRepository: mockAssetsRepository,
Expand All @@ -268,6 +298,7 @@ async function withAssetsService<ReturnValue>(
priceApiClient: mockPriceApiClient,
tokenApiClient: mockTokenApiClient,
snapClient: mockSnapClient,
coreMessenger: mockCoreMessenger,
});

return await testFunction({
Expand All @@ -279,6 +310,8 @@ async function withAssetsService<ReturnValue>(
mockPriceApiClient,
mockTokenApiClient,
mockSnapClient,
mockCoreMessenger,
setMigrationStage,
});
}

Expand Down Expand Up @@ -2843,6 +2876,7 @@ describe('AssetsService', () => {
decimals: 6,
rawAmount: '1',
uiAmount: '1',
iconUrl: '',
};

mockAssetsRepository.getByAccountId.mockResolvedValue([asset]);
Expand All @@ -2852,9 +2886,9 @@ describe('AssetsService', () => {
mockAssetsRepository.getByAccountIdAndAssetType.mockResolvedValue(
asset,
);
mockPriceApiClient.getFiatExchangeRates.mockResolvedValue({
usd: { value: 1 },
});
mockPriceApiClient.getFiatExchangeRates.mockResolvedValue(
MOCK_EXCHANGE_RATES,
);
mockPriceApiClient.getMultipleSpotPrices.mockResolvedValue(
createSpotPrices({
[KnownCaip19Id.TrxMainnet]: {
Expand Down Expand Up @@ -2888,7 +2922,8 @@ describe('AssetsService', () => {
);
expect(
byKeyringAccountId.some(
(savedAsset) => savedAsset.assetType === KnownCaip19Id.TrxMainnet,
(savedAsset: AssetEntity) =>
savedAsset.assetType === KnownCaip19Id.TrxMainnet,
),
).toBe(true);
const marketData = await assetsService.getMultipleTokensMarketData([
Expand All @@ -2905,4 +2940,45 @@ describe('AssetsService', () => {
);
});
});

describe('migration stage resolution', () => {
it('resolves remote feature flags on account asset reads without changing snap routing', async () => {
await withAssetsService(
async ({
assetsService,
mockAssetsRepository,
mockCoreMessenger,
setMigrationStage,
}) => {
setMigrationStage(
SnapsAssetsMigrationStage.ReadAssetsControllerWithFallback,
);

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

const asset = await assetsService.getAccountAssetByID(
mockAccount.id,
KnownCaip19Id.TrxMainnet,
);

expect(asset).toStrictEqual(snapAsset);
expect(mockCoreMessenger.call).toHaveBeenCalledWith(
'RemoteFeatureFlagController:getState',
);
},
);
});
});
});
Loading
Loading