From 994090c18d723db371471fa1be5d63619071210c Mon Sep 17 00:00:00 2001 From: Oscar Roche Date: Mon, 2 Feb 2026 10:56:47 +0100 Subject: [PATCH 1/5] feat: add rwa data to tokens in tokens controller --- packages/assets-controllers/CHANGELOG.md | 4 ++++ packages/assets-controllers/jest.config.js | 3 +++ packages/assets-controllers/src/TokensController.ts | 12 ++++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 9602b9c2910..4785c51a635 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add optional `rwaData` support when adding tokens in `TokensController` ([#7804](https://github.com/MetaMask/core/pull/7804)). + ## [98.0.0] ### Changed diff --git a/packages/assets-controllers/jest.config.js b/packages/assets-controllers/jest.config.js index add4e35680f..67fb4e6496f 100644 --- a/packages/assets-controllers/jest.config.js +++ b/packages/assets-controllers/jest.config.js @@ -32,4 +32,7 @@ module.exports = merge(baseConfig, { // We rely on `window` to make requests testEnvironment: '/jest.environment.js', + + // Watchman isn't available in some environments (e.g. sandboxed CI/containers) + watchman: false, }); diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index e2a6d348580..4c0df4a4eaf 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -51,6 +51,7 @@ import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standar import { fetchTokenMetadata, TOKEN_METADATA_NO_SUPPORT_ERROR, + TokenRwaData, } from './token-service'; import type { TokenListStateChange, @@ -288,6 +289,9 @@ export class TokensController extends BaseController< if (cachedToken && cachedToken.name && !token.name) { token.name = cachedToken.name; // Update the token name } + if (cachedToken?.rwaData) { + token.rwaData = cachedToken.rwaData; // Update the token RWA data + } } } } @@ -416,6 +420,7 @@ export class TokensController extends BaseController< * @param options.image - Image of the token. * @param options.interactingAddress - The address of the account to add a token to. * @param options.networkClientId - Network Client ID. + * @param options.rwaData - Optional RWA data for the token. * @returns Current token list. */ async addToken({ @@ -426,6 +431,7 @@ export class TokensController extends BaseController< image, interactingAddress, networkClientId, + rwaData, }: { address: string; symbol: string; @@ -434,6 +440,7 @@ export class TokensController extends BaseController< image?: string; interactingAddress?: string; networkClientId: NetworkClientId; + rwaData?: TokenRwaData; }): Promise { const releaseLock = await this.#mutex.acquire(); const { allTokens, allIgnoredTokens, allDetectedTokens } = this.state; @@ -473,7 +480,7 @@ export class TokensController extends BaseController< isERC721, aggregators: formatAggregatorNames(tokenMetadata?.aggregators ?? []), name, - ...(tokenMetadata?.rwaData && { rwaData: tokenMetadata.rwaData }), + ...(rwaData !== undefined && { rwaData }), }; const previousIndex = newTokens.findIndex( (token) => token.address.toLowerCase() === address.toLowerCase(), @@ -986,7 +993,7 @@ export class TokensController extends BaseController< await this.#requestApproval(suggestedAssetMeta); - const { address, symbol, decimals, name, image } = asset; + const { address, symbol, decimals, name, image, rwaData } = asset; await this.addToken({ address, symbol, @@ -995,6 +1002,7 @@ export class TokensController extends BaseController< image, interactingAddress: suggestedAssetMeta.interactingAddress, networkClientId, + rwaData, }); } From d00e8ab19b64f715b6c6c4f9a8e92ffa42be01b5 Mon Sep 17 00:00:00 2001 From: Oscar Roche Date: Mon, 2 Feb 2026 17:04:14 +0100 Subject: [PATCH 2/5] chore: fix changelog --- packages/assets-controllers/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 8e0804def40..1849d0b14de 100644 --- a/packages/assets-controllers/CHANGELOG.md +++ b/packages/assets-controllers/CHANGELOG.md @@ -7,11 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add optional `rwaData` support when adding tokens in `TokensController` ([#7804](https://github.com/MetaMask/core/pull/7804)). + ## [99.2.0] ### Added -- Add optional `rwaData` support when adding tokens in `TokensController` ([#7804](https://github.com/MetaMask/core/pull/7804)). - Add `HYPEREVM` support ([#7790](https://github.com/MetaMask/core/pull/7790)) - Add `HYPEREVM` in `SupportedTokenDetectionNetworks` - Add `HYPEREVM` in `SUPPORTED_NETWORKS_ACCOUNTS_API_V4` From fca7da246626c3ad74cfcd7a46a386ee42662234 Mon Sep 17 00:00:00 2001 From: Oscar Roche Date: Mon, 2 Feb 2026 17:38:41 +0100 Subject: [PATCH 3/5] chore: remove watchman false config line --- packages/assets-controllers/jest.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/assets-controllers/jest.config.js b/packages/assets-controllers/jest.config.js index 67fb4e6496f..add4e35680f 100644 --- a/packages/assets-controllers/jest.config.js +++ b/packages/assets-controllers/jest.config.js @@ -32,7 +32,4 @@ module.exports = merge(baseConfig, { // We rely on `window` to make requests testEnvironment: '/jest.environment.js', - - // Watchman isn't available in some environments (e.g. sandboxed CI/containers) - watchman: false, }); From c0bbc4b9c7ce0a5dfb0c484a146eaffa0f052184 Mon Sep 17 00:00:00 2001 From: Oscar Roche Date: Thu, 5 Feb 2026 16:38:30 +0100 Subject: [PATCH 4/5] Update packages/assets-controllers/src/TokensController.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Van Eyck --- packages/assets-controllers/src/TokensController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index 4c0df4a4eaf..2cf806322f9 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -285,7 +285,7 @@ export class TokensController extends BaseController< const tokens = updatedAllTokens[chainId as Hex][selectedAddress]; for (const [, token] of Object.entries(tokens)) { - const cachedToken = chainData[token.address]; + const cachedToken = chainData[token.address.toLowerCase()]; if (cachedToken && cachedToken.name && !token.name) { token.name = cachedToken.name; // Update the token name } From 6ee42c933c94bbbdfd83e8b6d88801d679bb8434 Mon Sep 17 00:00:00 2001 From: Oscar Roche Date: Thu, 5 Feb 2026 17:27:55 +0100 Subject: [PATCH 5/5] test: add tests for rwa data logic in tokens controller --- .../src/TokensController.test.ts | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index 50b9c00c499..a552e4904c6 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -34,6 +34,7 @@ import { v1 as uuidV1 } from 'uuid'; import { ERC20Standard } from './Standards/ERC20Standard'; import { ERC1155Standard } from './Standards/NftStandards/ERC1155/ERC1155Standard'; import { TOKEN_END_POINT_API } from './token-service'; +import type { TokenRwaData } from './token-service'; import type { Token } from './TokenRatesController'; import { TokensController } from './TokensController'; import type { @@ -1881,6 +1882,115 @@ describe('TokensController', () => { }, ); }); + + it('overwrites rwaData when re-adding tokens via addTokens', async () => { + await withController(async ({ controller }) => { + const existingRwaData: TokenRwaData = { + ticker: 'OLD', + }; + const updatedRwaData: TokenRwaData = { + ticker: 'NEW', + }; + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + rwaData: existingRwaData, + }, + ], + 'mainnet', + ); + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + rwaData: updatedRwaData, + }, + ], + 'mainnet', + ); + + expect( + controller.state.allTokens[ChainId.mainnet][ + defaultMockInternalAccount.address + ], + ).toStrictEqual([ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + rwaData: updatedRwaData, + }, + ]); + }); + }); + + it('clears rwaData when re-adding tokens without rwaData', async () => { + await withController(async ({ controller }) => { + const existingRwaData: TokenRwaData = { + ticker: 'OLD', + }; + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + rwaData: existingRwaData, + }, + ], + 'mainnet', + ); + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + }, + ], + 'mainnet', + ); + + expect( + controller.state.allTokens[ChainId.mainnet][ + defaultMockInternalAccount.address + ], + ).toStrictEqual([ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + }, + ]); + }); + }); }); describe('watchAsset', () => { @@ -3221,6 +3331,60 @@ describe('TokensController', () => { }); }); }); + + it('overwrites rwaData for tokens with cached rwaData', async () => { + await withController(async ({ controller, messenger }) => { + ContractMock.mockReturnValue( + buildMockEthersERC721Contract({ supportsInterface: false }), + ); + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'bar', + decimals: 2, + aggregators: [], + image: undefined, + name: undefined, + rwaData: { ticker: 'OLD' }, + }, + ], + 'mainnet', + ); + + messenger.publish( + 'TokenListController:stateChange', + { + tokensChainsCache: { + [ChainId.mainnet]: { + timestamp: 1, + data: { + '0x01': { + address: '0x01', + symbol: 'bar', + decimals: 2, + occurrences: 1, + name: 'BarName', + iconUrl: + 'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x01.png', + aggregators: ['Aave'], + rwaData: { ticker: 'NEW' }, + }, + }, + }, + }, + }, + [], + ); + + expect( + controller.state.allTokens[ChainId.mainnet][ + defaultMockInternalAccount.address + ][0].rwaData, + ).toStrictEqual({ ticker: 'NEW' }); + }); + }); }); describe('when selectedAccountId is not set or account not found', () => {