From bdcf0f074c4190a8c27f700f1d29c1bd587a4e3f Mon Sep 17 00:00:00 2001 From: salimtb Date: Thu, 7 Nov 2024 15:25:48 +0100 Subject: [PATCH] fix: fix multichain import token --- .../src/TokensController.test.ts | 138 ++++++++++++++++++ .../src/TokensController.ts | 26 +++- 2 files changed, 156 insertions(+), 8 deletions(-) diff --git a/packages/assets-controllers/src/TokensController.test.ts b/packages/assets-controllers/src/TokensController.test.ts index eb12ef587d..22f7c60a1a 100644 --- a/packages/assets-controllers/src/TokensController.test.ts +++ b/packages/assets-controllers/src/TokensController.test.ts @@ -198,6 +198,84 @@ describe('TokensController', () => { }); }); + it('should add tokens and update existing ones and detected tokens', async () => { + const selectedAddress = '0x0001'; + const selectedAccount = createMockInternalAccount({ + address: selectedAddress, + }); + await withController( + { + mockNetworkClientConfigurationsByNetworkClientId: { + networkClientId1: buildCustomNetworkClientConfiguration({ + chainId: '0x1', + }), + }, + mocks: { + getSelectedAccount: selectedAccount, + getAccount: selectedAccount, + }, + }, + async ({ controller }) => { + await controller.addDetectedTokens( + [ + { + address: '0x01', + symbol: 'barA', + decimals: 2, + }, + ], + { + selectedAddress: '0x0001', + chainId: '0x1', + }, + ); + + await controller.addTokens( + [ + { + address: '0x01', + symbol: 'barA', + decimals: 2, + aggregators: [], + name: 'Token1', + }, + { + address: '0x02', + symbol: 'barB', + decimals: 2, + aggregators: [], + name: 'Token2', + }, + ], + 'networkClientId1', + ); + + expect(controller.state.allTokens).toStrictEqual({ + '0x1': { + '0x0001': [ + { + address: '0x01', + symbol: 'barA', + decimals: 2, + aggregators: [], + name: 'Token1', + image: undefined, + }, + { + address: '0x02', + symbol: 'barB', + decimals: 2, + aggregators: [], + name: 'Token2', + image: undefined, + }, + ], + }, + }); + }, + ); + }); + it('should add detected tokens', async () => { await withController(async ({ controller }) => { await controller.addDetectedTokens([ @@ -2142,6 +2220,66 @@ describe('TokensController', () => { }, ); }); + + it('should clear allDetectedTokens under chain ID and selected address when a detected token is added to tokens list', async () => { + const selectedAddress = '0x1'; + const selectedAccount = createMockInternalAccount({ + address: selectedAddress, + }); + const tokenAddress = '0x01'; + const dummyDetectedTokens = [ + { + address: tokenAddress, + symbol: 'barA', + decimals: 2, + aggregators: [], + isERC721: undefined, + name: undefined, + image: undefined, + }, + ]; + const dummyTokens = [ + { + address: tokenAddress, + symbol: 'barA', + decimals: 2, + aggregators: [], + isERC721: undefined, + name: undefined, + image: undefined, + }, + ]; + + await withController( + { + options: { + chainId: ChainId.mainnet, + }, + mocks: { + getSelectedAccount: selectedAccount, + }, + }, + async ({ controller }) => { + // First, add detected tokens + await controller.addDetectedTokens(dummyDetectedTokens); + expect( + controller.state.allDetectedTokens[ChainId.mainnet][ + selectedAddress + ], + ).toStrictEqual(dummyDetectedTokens); + + // Now, add the same token to the tokens list + await controller.addTokens(dummyTokens); + + // Check that allDetectedTokens for the selected address is cleared + expect( + controller.state.allDetectedTokens[ChainId.mainnet][ + selectedAddress + ], + ).toStrictEqual([]); + }, + ); + }); }); describe('when TokenListController:stateChange is published', () => { diff --git a/packages/assets-controllers/src/TokensController.ts b/packages/assets-controllers/src/TokensController.ts index fe680cdd0d..fbf135799a 100644 --- a/packages/assets-controllers/src/TokensController.ts +++ b/packages/assets-controllers/src/TokensController.ts @@ -462,13 +462,16 @@ export class TokensController extends BaseController< */ async addTokens(tokensToImport: Token[], networkClientId?: NetworkClientId) { const releaseLock = await this.#mutex.acquire(); - const { tokens, detectedTokens, ignoredTokens } = this.state; + const { ignoredTokens, allDetectedTokens } = this.state; const importedTokensMap: { [key: string]: true } = {}; // Used later to dedupe imported tokens - const newTokensMap = tokens.reduce((output, current) => { - output[current.address] = current; - return output; - }, {} as { [address: string]: Token }); + const newTokensMap = Object.values(tokensToImport).reduce( + (output, token) => { + output[token.address] = token; + return output; + }, + {} as { [address: string]: Token }, + ); try { tokensToImport.forEach((tokenToAdd) => { const { address, symbol, decimals, image, aggregators, name } = @@ -488,9 +491,6 @@ export class TokensController extends BaseController< }); const newTokens = Object.values(newTokensMap); - const newDetectedTokens = detectedTokens.filter( - (token) => !importedTokensMap[token.address.toLowerCase()], - ); const newIgnoredTokens = ignoredTokens.filter( (tokenAddress) => !newTokensMap[tokenAddress.toLowerCase()], ); @@ -503,6 +503,16 @@ export class TokensController extends BaseController< ).configuration.chainId; } + const newDetectedTokens = + interactingChainId && + allDetectedTokens[interactingChainId]?.[this.#getSelectedAddress()] + ? allDetectedTokens[interactingChainId]?.[ + this.#getSelectedAddress() + ].filter( + (token: Token) => !importedTokensMap[token.address.toLowerCase()], + ) + : []; + const { newAllTokens, newAllDetectedTokens, newAllIgnoredTokens } = this.#getNewAllTokensState({ newTokens,