diff --git a/packages/assets-controllers/CHANGELOG.md b/packages/assets-controllers/CHANGELOG.md index 2d9a48a21a2..642e1ef0c42 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)). + ### Changed - Bump `@metamask/profile-sync-controller` from `^27.0.0` to `^27.1.0` ([#7849](https://github.com/MetaMask/core/pull/7849)) 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', () => { diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index e2a6d348580..2cf806322f9 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, @@ -284,10 +285,13 @@ 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 } + 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, }); }