From 55bb7c341632139a54d14324d33827663327a690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Fri, 30 Aug 2024 14:49:22 +0200 Subject: [PATCH 1/3] fix: treat BTC as known network --- .../handlers/wallet_addEthereumChain.test.ts | 72 ++++++++++--------- .../handlers/wallet_addEthereumChain.ts | 10 ++- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts index 2832ab46..83446f65 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts @@ -105,7 +105,30 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = }, ], }; - openExtensionNewWindow; + const supportedNetwork = { + caipId: 'eip155:43113', + chainId: 43113, + chainName: 'Avalanche', + vmName: NetworkVMType.EVM, + primaryColor: 'black', + rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', + explorerUrl: 'https://snowtrace.io/', + logoUri: '', + networkToken: { + symbol: 'AVAX', + decimals: 18, + description: '', + name: 'AVAX', + logoUri: '', + }, + isTestnet: false, + }; + + jest + .mocked(mockNetworkService.getNetwork) + .mockResolvedValueOnce(mockActiveNetwork as any) + .mockResolvedValueOnce(supportedNetwork); + const result = await handler.handleUnauthenticated(buildRpcCall(request)); expect(openExtensionNewWindow).toHaveBeenCalledTimes(1); @@ -118,24 +141,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = actionId: 'uuid', scope: 'eip155:43113', displayData: { - network: { - caipId: 'eip155:43113', - chainId: 43113, - chainName: 'Avalanche', - vmName: NetworkVMType.EVM, - primaryColor: 'black', - rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', - explorerUrl: 'https://snowtrace.io/', - logoUri: '', - networkToken: { - symbol: 'AVAX', - decimals: 18, - description: '', - name: 'AVAX', - logoUri: '', - }, - isTestnet: false, - }, + network: supportedNetwork, }, popupWindowId: 123, }); @@ -536,19 +542,18 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = it('does not opens approval dialog and switch to a known network if the request is from core web', async () => { jest.mocked(isCoreWeb).mockResolvedValue(true); + const supportedNetwork = { + chainId: '0xa869', // 43113 + chainName: 'Avalanche', + nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 }, + rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'], + blockExplorerUrls: ['https://snowtrace.io/'], + iconUrls: ['logo.png'], + }; const request = { id: '852', method: DAppProviderRequest.WALLET_ADD_CHAIN, - params: [ - { - chainId: '0xa869', // 43113 - chainName: 'Avalanche', - nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 }, - rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'], - blockExplorerUrls: ['https://snowtrace.io/'], - iconUrls: ['logo.png'], - }, - ], + params: [supportedNetwork], site: { domain: 'core.app', name: 'Core', @@ -556,6 +561,11 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = }, }; + jest + .mocked(mockNetworkService.getNetwork) + .mockResolvedValueOnce(mockActiveNetwork as any) + .mockResolvedValueOnce(supportedNetwork as any); + const result = await handler.handleAuthenticated(buildRpcCall(request)); expect(result).toEqual({ @@ -568,9 +578,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.setNetwork).toHaveBeenCalledTimes(1); expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( 'core.app', - expect.objectContaining({ - chainId: 43113, - }) + supportedNetwork ); }); diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.ts b/src/background/services/network/handlers/wallet_addEthereumChain.ts index 0cbb5af4..b4d4a07a 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.ts @@ -118,8 +118,14 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { } const skipApproval = await isCoreWeb(request); + const targetNetwork = chainRequestedIsSupported + ? ((await this.networkService.getNetwork( + requestedChainId + )) as NetworkWithCaipId) + : customNetwork; + if (skipApproval) { - await this.actionHandler(chains, customNetwork, request.site.domain); + await this.actionHandler(chains, targetNetwork, request.site.domain); return { ...request, result: null }; } @@ -128,7 +134,7 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { ...request, scope, displayData: { - network: customNetwork, + network: targetNetwork, }, }; From 0f732c3852378fc40ddbda78c1aa7dad7563630c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Fri, 30 Aug 2024 17:01:10 +0200 Subject: [PATCH 2/3] refactor: change .setNetwork() to accept chainId instead of network --- .../services/network/NetworkService.test.ts | 144 +++++++++++------- .../services/network/NetworkService.ts | 19 ++- .../handlers/avalanche_setDeveloperMode.ts | 9 +- .../network/handlers/saveCustomNetwork.ts | 8 +- .../network/handlers/setActiveNetwork.ts | 14 +- .../network/handlers/setDeveloperMode.ts | 18 +-- .../handlers/wallet_addEthereumChain.test.ts | 74 ++++----- .../handlers/wallet_addEthereumChain.ts | 12 +- .../handlers/wallet_switchEthereumChain.ts | 4 +- .../services/onboarding/finalizeOnboarding.ts | 6 +- 10 files changed, 158 insertions(+), 150 deletions(-) diff --git a/src/background/services/network/NetworkService.test.ts b/src/background/services/network/NetworkService.test.ts index 11bb456f..23b680ff 100644 --- a/src/background/services/network/NetworkService.test.ts +++ b/src/background/services/network/NetworkService.test.ts @@ -103,6 +103,38 @@ describe('background/services/network/NetworkService', () => { const avaxMainnet = mockNetwork(NetworkVMType.EVM, false, { chainId: 43114, }); + const leetNetwork = mockNetwork(NetworkVMType.EVM, false, { + chainId: 1337, + }); + const btcNetwork = mockNetwork(NetworkVMType.BITCOIN, false, { + chainId: ChainId.BITCOIN, + }); + const sepolia = mockNetwork(NetworkVMType.EVM, true, { + chainId: 11155111, + }); + + const mockChainList = (instance: NetworkService) => { + // eslint-disable-next-line + // @ts-ignore + jest.spyOn(instance._rawNetworks, 'promisify').mockResolvedValue( + Promise.resolve({ + [ethMainnet.chainId]: ethMainnet, + [avaxMainnet.chainId]: avaxMainnet, + [btcNetwork.chainId]: btcNetwork, + [leetNetwork.chainId]: leetNetwork, + [sepolia.chainId]: sepolia, + }) + ); + jest.spyOn(instance.allNetworks, 'promisify').mockResolvedValue( + Promise.resolve({ + [ethMainnet.chainId]: ethMainnet, + [avaxMainnet.chainId]: avaxMainnet, + [btcNetwork.chainId]: btcNetwork, + [leetNetwork.chainId]: leetNetwork, + [sepolia.chainId]: sepolia, + }) + ); + }; beforeAll(() => { process.env = { @@ -116,26 +148,27 @@ describe('background/services/network/NetworkService', () => { jest.resetAllMocks(); jest.mocked(getChainsAndTokens).mockResolvedValue({ - [43114]: { - chainName: 'test chain', - chainId: 123, - vmName: NetworkVMType.EVM, - rpcUrl: 'https://rpcurl.example', - explorerUrl: 'https://explorer.url', - networkToken: { - name: 'test network token', - symbol: 'TNT', - description: '', - decimals: 18, - logoUri: '', - }, - logoUri: '', - primaryColor: 'blue', - isTestnet: false, - }, + // [43114]: { + // chainName: 'test chain x', + // chainId: 123, + // vmName: NetworkVMType.EVM, + // rpcUrl: 'https://rpcurl.example', + // explorerUrl: 'https://explorer.url', + // networkToken: { + // name: 'test network token', + // symbol: 'TNT', + // description: '', + // decimals: 18, + // logoUri: '', + // }, + // logoUri: '', + // primaryColor: 'blue', + // isTestnet: false, + // }, }); jest.mocked(FetchRequest).mockImplementation((url) => ({ url } as any)); + mockChainList(service); }); afterAll(() => { @@ -143,18 +176,12 @@ describe('background/services/network/NetworkService', () => { }); describe('.getInitialNetworkForDapp()', () => { - const leetNetwork = mockNetwork(NetworkVMType.EVM, false, { - chainId: 1337, - }); - const btcNetwork = mockNetwork(NetworkVMType.BITCOIN, false, { - chainId: ChainId.BITCOIN, - }); - const chainlist = { [ethMainnet.chainId]: ethMainnet, [avaxMainnet.chainId]: avaxMainnet, [leetNetwork.chainId]: leetNetwork, [btcNetwork.chainId]: btcNetwork, + [sepolia.chainId]: sepolia, }; it('returns the most recently active network for given domain', async () => { @@ -181,7 +208,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, leetNetwork); + await service.setNetwork(runtime.id, leetNetwork.chainId); const network = await service.getInitialNetworkForDapp('test.app'); @@ -196,7 +223,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, btcNetwork); + await service.setNetwork(runtime.id, btcNetwork.chainId); const network = await service.getInitialNetworkForDapp('core.app'); @@ -211,7 +238,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, btcNetwork); + await service.setNetwork(runtime.id, btcNetwork.chainId); const network = await service.getInitialNetworkForDapp('test.app'); @@ -240,8 +267,10 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.save.mockResolvedValue(); }); + // it('defaults to saved config instead of accepting'); + it('persists current network for given domain', async () => { - await service.setNetwork('test.app', ethMainnet); + await service.setNetwork('test.app', ethMainnet.chainId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -258,7 +287,7 @@ describe('background/services/network/NetworkService', () => { service.dappScopeChanged.addOnce(listener); - await service.setNetwork('test.app', ethMainnet); + await service.setNetwork('test.app', ethMainnet.chainId); expect(listener).toHaveBeenCalledWith({ domain: 'test.app', @@ -267,17 +296,14 @@ describe('background/services/network/NetworkService', () => { }); describe('when dApp network switch results in environment change', () => { - const sepolia = mockNetwork(NetworkVMType.EVM, true, { - chainId: 11155111, - }); - beforeEach(async () => { service = new NetworkService( storageServiceMock, featureFlagsServiceMock ); + mockChainList(service); // Set Ethereum Mainnet directly for the frontend - await service.setNetwork(runtime.id, ethMainnet); + await service.setNetwork(runtime.id, ethMainnet.chainId); expect(service.uiActiveNetwork).toEqual(ethMainnet); }); @@ -285,7 +311,7 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.load.mockResolvedValueOnce({ [runtime.id]: sepolia.caipId, }); - await service.setNetwork('app.uniswap.io', sepolia); + await service.setNetwork('app.uniswap.io', sepolia.chainId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, expect.objectContaining({ @@ -300,7 +326,7 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.load.mockResolvedValueOnce({ [runtime.id]: sepolia.caipId, }); - await service.setNetwork('app.uniswap.io', sepolia); + await service.setNetwork('app.uniswap.io', sepolia.chainId); expect(service.uiActiveNetwork).toEqual(sepolia); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -318,7 +344,7 @@ describe('background/services/network/NetworkService', () => { service.developerModeChanged.addOnce(onDevModeChange); - await service.setNetwork('app.uniswap.io', sepolia); + await service.setNetwork('app.uniswap.io', sepolia.chainId); expect(service.uiActiveNetwork).toEqual(sepolia); expect(onDevModeChange).toHaveBeenCalledWith(true); @@ -327,7 +353,7 @@ describe('background/services/network/NetworkService', () => { describe('when changing network for synchronized dApps', () => { it('uses the extension ID as domain', async () => { - await service.setNetwork('core.app', ethMainnet); + await service.setNetwork('core.app', ethMainnet.chainId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -340,12 +366,18 @@ describe('background/services/network/NetworkService', () => { }); it('changes the network for the extension ui as well', async () => { + jest.spyOn(service.allNetworks, 'promisify').mockResolvedValue( + Promise.resolve({ + [ethMainnet.chainId]: ethMainnet, + [avaxMainnet.chainId]: avaxMainnet, + }) + ); // Set Ethereum directly for the frontend - await service.setNetwork(runtime.id, ethMainnet); + await service.setNetwork(runtime.id, ethMainnet.chainId); expect(service.uiActiveNetwork).toEqual(ethMainnet); // Set C-Chain for core.app and expect it to change also for the extension UI - await service.setNetwork('core.app', avaxMainnet); + await service.setNetwork('core.app', avaxMainnet.chainId); expect(service.uiActiveNetwork).toEqual(avaxMainnet); }); @@ -353,8 +385,13 @@ describe('background/services/network/NetworkService', () => { const listener = jest.fn(); service.dappScopeChanged.addOnce(listener); + jest.spyOn(service.allNetworks, 'promisify').mockResolvedValue( + Promise.resolve({ + [ethMainnet.chainId]: ethMainnet, + }) + ); - await service.setNetwork('core.app', ethMainnet); + await service.setNetwork('core.app', ethMainnet.chainId); expect(listener).toHaveBeenCalledWith({ domain: runtime.id, @@ -415,8 +452,13 @@ describe('background/services/network/NetworkService', () => { jest.spyOn(networkService._rawNetworks, 'promisify').mockResolvedValue({ 1337: activeNetwork, }); + jest.spyOn(networkService.allNetworks, 'promisify').mockResolvedValue( + Promise.resolve({ + 1337: activeNetwork, + }) + ); - await networkService.setNetwork(runtime.id, activeNetwork); + await networkService.setNetwork(runtime.id, activeNetwork.chainId); await networkService.updateNetworkOverrides({ caipId: 'eip155:1337', @@ -437,24 +479,25 @@ describe('background/services/network/NetworkService', () => { describe('saveCustomNetwork()', () => { let customNetwork; - beforeEach(() => { + beforeEach(async () => { customNetwork = mockNetwork(NetworkVMType.EVM, false); }); it('should throw an error because of the chainlist failed to load', async () => { jest // eslint-disable-next-line - // @ts-expect-error + // @ts-ignore .spyOn(service._rawNetworks, 'promisify') - .mockResolvedValue(Promise.resolve(undefined)); - expect(service.saveCustomNetwork(customNetwork)).rejects.toThrow( + .mockResolvedValueOnce(Promise.resolve(undefined)); + + await expect(service.saveCustomNetwork(customNetwork)).rejects.toThrow( 'chainlist failed to load' ); }); it('should throw an error because of duplicated ID', async () => { const newCustomNetwork = { ...customNetwork, chainId: 43114 }; - expect(service.saveCustomNetwork(newCustomNetwork)).rejects.toThrow( + await expect(service.saveCustomNetwork(newCustomNetwork)).rejects.toThrow( 'chain ID already exists' ); }); @@ -476,11 +519,8 @@ describe('background/services/network/NetworkService', () => { ...customNetwork, rpcUrl: newRpcUrl, }; - await service.saveCustomNetwork(newCustomNetwork); - - // Set it as active - const savedActiveNetwork = await service.getNetwork( - newCustomNetwork.chainId + const savedActiveNetwork = await service.saveCustomNetwork( + newCustomNetwork ); expect(savedActiveNetwork?.rpcUrl).toBe(newRpcUrl); diff --git a/src/background/services/network/NetworkService.ts b/src/background/services/network/NetworkService.ts index 2e80240a..e822c240 100644 --- a/src/background/services/network/NetworkService.ts +++ b/src/background/services/network/NetworkService.ts @@ -149,18 +149,25 @@ export class NetworkService implements OnLock, OnStorageReady { ); } - async setNetwork(domain: string, selectedNetwork: NetworkWithCaipId) { + async setNetwork(domain: string, chainId: number) { const isSynced = isSyncDomain(domain); + // For supported networks, use config from saved chainlist + // instead of relying on payload that may come from a 3rd party: + const targetNetwork = await this.getNetwork(chainId); + if (!targetNetwork) { + throw new Error(`Network not found: ${chainId}`); + } + const changesEnvironment = Boolean(this._uiActiveNetwork?.isTestnet) !== - Boolean(selectedNetwork.isTestnet); + Boolean(targetNetwork.isTestnet); if (isSynced || changesEnvironment) { - this.uiActiveNetwork = selectedNetwork; + this.uiActiveNetwork = targetNetwork; } // Save scope for requesting dApp - await this.#updateDappScopes({ [domain]: selectedNetwork.caipId }); + await this.#updateDappScopes({ [domain]: targetNetwork.caipId }); // If change resulted in an environment switch, also notify other dApps if (changesEnvironment) { @@ -173,7 +180,7 @@ export class NetworkService implements OnLock, OnStorageReady { .map(([savedDomain]) => [ savedDomain, chainIdToCaip( - selectedNetwork.isTestnet + targetNetwork.isTestnet ? ChainId.AVALANCHE_TESTNET_ID : ChainId.AVALANCHE_MAINNET_ID ), @@ -679,7 +686,7 @@ export class NetworkService implements OnLock, OnStorageReady { ); if (network) { - await this.setNetwork(runtime.id, network); + await this.setNetwork(runtime.id, network.chainId); } } diff --git a/src/background/services/network/handlers/avalanche_setDeveloperMode.ts b/src/background/services/network/handlers/avalanche_setDeveloperMode.ts index 83b81df7..a64e039c 100644 --- a/src/background/services/network/handlers/avalanche_setDeveloperMode.ts +++ b/src/background/services/network/handlers/avalanche_setDeveloperMode.ts @@ -71,16 +71,11 @@ export class AvalancheSetDeveloperModeHandler extends DAppRequestHandler { throw new Error('Unrecognized domain'); } - const network = await this.networkService.getNetwork( + await this.networkService.setNetwork( + domain, isTestmode ? ChainId.AVALANCHE_TESTNET_ID : ChainId.AVALANCHE_MAINNET_ID ); - if (!network) { - throw new Error('Target network not found'); - } - - await this.networkService.setNetwork(domain, network); - onSuccess(null); } catch (e) { onError(e); diff --git a/src/background/services/network/handlers/saveCustomNetwork.ts b/src/background/services/network/handlers/saveCustomNetwork.ts index 492c706e..a1838f7d 100644 --- a/src/background/services/network/handlers/saveCustomNetwork.ts +++ b/src/background/services/network/handlers/saveCustomNetwork.ts @@ -1,10 +1,10 @@ import { ExtensionRequest } from '@src/background/connections/extensionConnection/models'; import { ExtensionRequestHandler } from '@src/background/connections/models'; -import { resolve } from '@src/utils/promiseResolver'; import { injectable } from 'tsyringe'; import { NetworkService } from '../NetworkService'; import { CustomNetworkPayload } from '../models'; import { runtime } from 'webextension-polyfill'; +import { resolve } from '@avalabs/core-utils-sdk'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.NETWORK_SAVE_CUSTOM, @@ -43,14 +43,14 @@ export class SaveCustomNetworkHandler implements HandlerType { this.networkService.saveCustomNetwork(network) ); - if (err) { + if (err || !addedNetwork) { return { ...request, - error: err.toString(), + error: err?.toString() ?? 'Adding custom network failed', }; } - await this.networkService.setNetwork(runtime.id, addedNetwork); + await this.networkService.setNetwork(runtime.id, addedNetwork.chainId); return { ...request, diff --git a/src/background/services/network/handlers/setActiveNetwork.ts b/src/background/services/network/handlers/setActiveNetwork.ts index fb4ec53d..c1bad55d 100644 --- a/src/background/services/network/handlers/setActiveNetwork.ts +++ b/src/background/services/network/handlers/setActiveNetwork.ts @@ -6,6 +6,7 @@ import { ExtensionRequest } from '@src/background/connections/extensionConnectio import { ExtensionRequestHandler } from '@src/background/connections/models'; import { NetworkService } from '../NetworkService'; +import { caipToChainId } from '@src/utils/caipConversion'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.NETWORK_SET_ACTIVE, @@ -21,19 +22,8 @@ export class SetActiveNetworkHandler implements HandlerType { handle: HandlerType['handle'] = async ({ request }) => { const [scope] = request.params; - const [network, networkErr] = await resolve( - this.networkService.getNetwork(scope) - ); - - if (networkErr || !network) { - return { - ...request, - error: 'Ttarget network not found', - }; - } - const [, err] = await resolve( - this.networkService.setNetwork(runtime.id, network) + this.networkService.setNetwork(runtime.id, caipToChainId(scope)) ); if (err) { diff --git a/src/background/services/network/handlers/setDeveloperMode.ts b/src/background/services/network/handlers/setDeveloperMode.ts index 5c2f9564..5db887b7 100644 --- a/src/background/services/network/handlers/setDeveloperMode.ts +++ b/src/background/services/network/handlers/setDeveloperMode.ts @@ -22,19 +22,13 @@ export class SetDevelopermodeNetworkHandler implements HandlerType { handle: HandlerType['handle'] = async ({ request }) => { const [enableDeveloperMode] = request.params; - - const network = await this.networkService.getNetwork( - enableDeveloperMode - ? ChainId.AVALANCHE_TESTNET_ID - : ChainId.AVALANCHE_MAINNET_ID - ); - - if (!network) { - throw new Error('Target network not found'); - } - const [, err] = await resolve( - this.networkService.setNetwork(runtime.id, network) + this.networkService.setNetwork( + runtime.id, + enableDeveloperMode + ? ChainId.AVALANCHE_TESTNET_ID + : ChainId.AVALANCHE_MAINNET_ID + ) ); if (err) { diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts index 83446f65..1fffb409 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts @@ -105,30 +105,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = }, ], }; - const supportedNetwork = { - caipId: 'eip155:43113', - chainId: 43113, - chainName: 'Avalanche', - vmName: NetworkVMType.EVM, - primaryColor: 'black', - rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', - explorerUrl: 'https://snowtrace.io/', - logoUri: '', - networkToken: { - symbol: 'AVAX', - decimals: 18, - description: '', - name: 'AVAX', - logoUri: '', - }, - isTestnet: false, - }; - - jest - .mocked(mockNetworkService.getNetwork) - .mockResolvedValueOnce(mockActiveNetwork as any) - .mockResolvedValueOnce(supportedNetwork); - + openExtensionNewWindow; const result = await handler.handleUnauthenticated(buildRpcCall(request)); expect(openExtensionNewWindow).toHaveBeenCalledTimes(1); @@ -141,7 +118,24 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = actionId: 'uuid', scope: 'eip155:43113', displayData: { - network: supportedNetwork, + network: { + caipId: 'eip155:43113', + chainId: 43113, + chainName: 'Avalanche', + vmName: NetworkVMType.EVM, + primaryColor: 'black', + rpcUrl: 'https://api.avax.network/ext/bc/C/rpc', + explorerUrl: 'https://snowtrace.io/', + logoUri: '', + networkToken: { + symbol: 'AVAX', + decimals: 18, + description: '', + name: 'AVAX', + logoUri: '', + }, + isTestnet: false, + }, }, popupWindowId: 123, }); @@ -542,18 +536,19 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = it('does not opens approval dialog and switch to a known network if the request is from core web', async () => { jest.mocked(isCoreWeb).mockResolvedValue(true); - const supportedNetwork = { - chainId: '0xa869', // 43113 - chainName: 'Avalanche', - nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 }, - rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'], - blockExplorerUrls: ['https://snowtrace.io/'], - iconUrls: ['logo.png'], - }; const request = { id: '852', method: DAppProviderRequest.WALLET_ADD_CHAIN, - params: [supportedNetwork], + params: [ + { + chainId: '0xa869', // 43113 + chainName: 'Avalanche', + nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 }, + rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'], + blockExplorerUrls: ['https://snowtrace.io/'], + iconUrls: ['logo.png'], + }, + ], site: { domain: 'core.app', name: 'Core', @@ -561,11 +556,6 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = }, }; - jest - .mocked(mockNetworkService.getNetwork) - .mockResolvedValueOnce(mockActiveNetwork as any) - .mockResolvedValueOnce(supportedNetwork as any); - const result = await handler.handleAuthenticated(buildRpcCall(request)); expect(result).toEqual({ @@ -578,7 +568,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.setNetwork).toHaveBeenCalledTimes(1); expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( 'core.app', - supportedNetwork + 43113 ); }); @@ -795,7 +785,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( mockPendingAction.site?.domain, - mockPendingAction.displayData.network + mockPendingAction.displayData.network.chainId ); expect(mockNetworkService.saveCustomNetwork).toHaveBeenCalledTimes(1); expect(mockNetworkService.saveCustomNetwork).toHaveBeenCalledWith({ @@ -858,7 +848,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.saveCustomNetwork).not.toHaveBeenCalled(); expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( mockPendingAction.site?.domain, - network + network.chainId ); expect(successHandler).toHaveBeenCalledTimes(1); diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.ts b/src/background/services/network/handlers/wallet_addEthereumChain.ts index b4d4a07a..fa8c7ee1 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.ts @@ -118,14 +118,8 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { } const skipApproval = await isCoreWeb(request); - const targetNetwork = chainRequestedIsSupported - ? ((await this.networkService.getNetwork( - requestedChainId - )) as NetworkWithCaipId) - : customNetwork; - if (skipApproval) { - await this.actionHandler(chains, targetNetwork, request.site.domain); + await this.actionHandler(chains, customNetwork, request.site.domain); return { ...request, result: null }; } @@ -134,7 +128,7 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { ...request, scope, displayData: { - network: targetNetwork, + network: customNetwork, }, }; @@ -194,7 +188,7 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { await this.networkService.saveCustomNetwork(network); } - await this.networkService.setNetwork(domain, network); + await this.networkService.setNetwork(domain, network.chainId); } onActionApproved = async ( diff --git a/src/background/services/network/handlers/wallet_switchEthereumChain.ts b/src/background/services/network/handlers/wallet_switchEthereumChain.ts index c376ab76..bbfd161b 100644 --- a/src/background/services/network/handlers/wallet_switchEthereumChain.ts +++ b/src/background/services/network/handlers/wallet_switchEthereumChain.ts @@ -55,7 +55,7 @@ export class WalletSwitchEthereumChainHandler extends DAppRequestHandler { if (skipApproval) { await this.networkService.setNetwork( request.site.domain, - supportedNetwork + supportedNetwork.chainId ); return { ...request, result: null }; } @@ -98,7 +98,7 @@ export class WalletSwitchEthereumChainHandler extends DAppRequestHandler { try { await this.networkService.setNetwork( pendingAction.site.domain, - pendingAction.displayData.network + pendingAction.displayData.network.chainId ); onSuccess(null); diff --git a/src/background/services/onboarding/finalizeOnboarding.ts b/src/background/services/onboarding/finalizeOnboarding.ts index 0f778972..cfc49fc5 100644 --- a/src/background/services/onboarding/finalizeOnboarding.ts +++ b/src/background/services/onboarding/finalizeOnboarding.ts @@ -26,10 +26,8 @@ export async function finalizeOnboarding({ await networkService.addFavoriteNetwork(ChainId.BITCOIN); await networkService.addFavoriteNetwork(ChainId.ETHEREUM_HOMESTEAD); - await networkService.setNetwork( - runtime.id, - await networkService.getAvalancheNetwork() - ); + const cChain = await networkService.getAvalancheNetwork(); + await networkService.setNetwork(runtime.id, cChain.chainId); const allAccounts = accountsService.getAccounts(); const addedAccounts = allAccounts.primary[walletId]; From c198a0766f9fff9cac854735758292e8f462797d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Leszczyk?= Date: Fri, 30 Aug 2024 18:55:54 +0200 Subject: [PATCH 3/3] refactor: migrate from chainId to caipId as setNetwork() param --- .../services/network/NetworkService.test.ts | 51 ++++++------------- .../services/network/NetworkService.ts | 8 +-- .../handlers/avalanche_setDeveloperMode.ts | 7 ++- .../network/handlers/saveCustomNetwork.ts | 2 +- .../network/handlers/setActiveNetwork.ts | 3 +- .../network/handlers/setDeveloperMode.ts | 9 ++-- .../handlers/wallet_addEthereumChain.test.ts | 8 +-- .../handlers/wallet_addEthereumChain.ts | 2 +- .../handlers/wallet_switchEthereumChain.ts | 4 +- .../services/onboarding/finalizeOnboarding.ts | 2 +- 10 files changed, 43 insertions(+), 53 deletions(-) diff --git a/src/background/services/network/NetworkService.test.ts b/src/background/services/network/NetworkService.test.ts index 23b680ff..b8e0b337 100644 --- a/src/background/services/network/NetworkService.test.ts +++ b/src/background/services/network/NetworkService.test.ts @@ -147,25 +147,7 @@ describe('background/services/network/NetworkService', () => { beforeEach(() => { jest.resetAllMocks(); - jest.mocked(getChainsAndTokens).mockResolvedValue({ - // [43114]: { - // chainName: 'test chain x', - // chainId: 123, - // vmName: NetworkVMType.EVM, - // rpcUrl: 'https://rpcurl.example', - // explorerUrl: 'https://explorer.url', - // networkToken: { - // name: 'test network token', - // symbol: 'TNT', - // description: '', - // decimals: 18, - // logoUri: '', - // }, - // logoUri: '', - // primaryColor: 'blue', - // isTestnet: false, - // }, - }); + jest.mocked(getChainsAndTokens).mockResolvedValue({}); jest.mocked(FetchRequest).mockImplementation((url) => ({ url } as any)); mockChainList(service); @@ -208,7 +190,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, leetNetwork.chainId); + await service.setNetwork(runtime.id, leetNetwork.caipId); const network = await service.getInitialNetworkForDapp('test.app'); @@ -223,7 +205,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, btcNetwork.chainId); + await service.setNetwork(runtime.id, btcNetwork.caipId); const network = await service.getInitialNetworkForDapp('core.app'); @@ -238,7 +220,7 @@ describe('background/services/network/NetworkService', () => { .mockResolvedValueOnce(chainlist); await service.init(); - await service.setNetwork(runtime.id, btcNetwork.chainId); + await service.setNetwork(runtime.id, btcNetwork.caipId); const network = await service.getInitialNetworkForDapp('test.app'); @@ -267,10 +249,8 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.save.mockResolvedValue(); }); - // it('defaults to saved config instead of accepting'); - it('persists current network for given domain', async () => { - await service.setNetwork('test.app', ethMainnet.chainId); + await service.setNetwork('test.app', ethMainnet.caipId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -287,7 +267,7 @@ describe('background/services/network/NetworkService', () => { service.dappScopeChanged.addOnce(listener); - await service.setNetwork('test.app', ethMainnet.chainId); + await service.setNetwork('test.app', ethMainnet.caipId); expect(listener).toHaveBeenCalledWith({ domain: 'test.app', @@ -303,7 +283,7 @@ describe('background/services/network/NetworkService', () => { ); mockChainList(service); // Set Ethereum Mainnet directly for the frontend - await service.setNetwork(runtime.id, ethMainnet.chainId); + await service.setNetwork(runtime.id, ethMainnet.caipId); expect(service.uiActiveNetwork).toEqual(ethMainnet); }); @@ -311,7 +291,7 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.load.mockResolvedValueOnce({ [runtime.id]: sepolia.caipId, }); - await service.setNetwork('app.uniswap.io', sepolia.chainId); + await service.setNetwork('app.uniswap.io', sepolia.caipId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, expect.objectContaining({ @@ -326,7 +306,7 @@ describe('background/services/network/NetworkService', () => { storageServiceMock.load.mockResolvedValueOnce({ [runtime.id]: sepolia.caipId, }); - await service.setNetwork('app.uniswap.io', sepolia.chainId); + await service.setNetwork('app.uniswap.io', sepolia.caipId); expect(service.uiActiveNetwork).toEqual(sepolia); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -344,7 +324,7 @@ describe('background/services/network/NetworkService', () => { service.developerModeChanged.addOnce(onDevModeChange); - await service.setNetwork('app.uniswap.io', sepolia.chainId); + await service.setNetwork('app.uniswap.io', sepolia.caipId); expect(service.uiActiveNetwork).toEqual(sepolia); expect(onDevModeChange).toHaveBeenCalledWith(true); @@ -353,7 +333,7 @@ describe('background/services/network/NetworkService', () => { describe('when changing network for synchronized dApps', () => { it('uses the extension ID as domain', async () => { - await service.setNetwork('core.app', ethMainnet.chainId); + await service.setNetwork('core.app', ethMainnet.caipId); expect(storageServiceMock.save).toHaveBeenCalledWith( NETWORK_STORAGE_KEY, @@ -373,11 +353,11 @@ describe('background/services/network/NetworkService', () => { }) ); // Set Ethereum directly for the frontend - await service.setNetwork(runtime.id, ethMainnet.chainId); + await service.setNetwork(runtime.id, ethMainnet.caipId); expect(service.uiActiveNetwork).toEqual(ethMainnet); // Set C-Chain for core.app and expect it to change also for the extension UI - await service.setNetwork('core.app', avaxMainnet.chainId); + await service.setNetwork('core.app', avaxMainnet.caipId); expect(service.uiActiveNetwork).toEqual(avaxMainnet); }); @@ -391,7 +371,7 @@ describe('background/services/network/NetworkService', () => { }) ); - await service.setNetwork('core.app', ethMainnet.chainId); + await service.setNetwork('core.app', ethMainnet.caipId); expect(listener).toHaveBeenCalledWith({ domain: runtime.id, @@ -410,6 +390,7 @@ describe('background/services/network/NetworkService', () => { it('saves custom RPC headers', async () => { const overrides = { chainId: 1337, + caipId: 'eip155:1337', customRpcHeaders: { 'X-Glacier-Api-Key': 'my-elite-api-key', }, @@ -458,7 +439,7 @@ describe('background/services/network/NetworkService', () => { }) ); - await networkService.setNetwork(runtime.id, activeNetwork.chainId); + await networkService.setNetwork(runtime.id, activeNetwork.caipId); await networkService.updateNetworkOverrides({ caipId: 'eip155:1337', diff --git a/src/background/services/network/NetworkService.ts b/src/background/services/network/NetworkService.ts index e822c240..8680a17c 100644 --- a/src/background/services/network/NetworkService.ts +++ b/src/background/services/network/NetworkService.ts @@ -149,13 +149,13 @@ export class NetworkService implements OnLock, OnStorageReady { ); } - async setNetwork(domain: string, chainId: number) { + async setNetwork(domain: string, caipId: string) { const isSynced = isSyncDomain(domain); // For supported networks, use config from saved chainlist // instead of relying on payload that may come from a 3rd party: - const targetNetwork = await this.getNetwork(chainId); + const targetNetwork = await this.getNetwork(caipId); if (!targetNetwork) { - throw new Error(`Network not found: ${chainId}`); + throw new Error(`Network not found: ${caipId}`); } const changesEnvironment = @@ -686,7 +686,7 @@ export class NetworkService implements OnLock, OnStorageReady { ); if (network) { - await this.setNetwork(runtime.id, network.chainId); + await this.setNetwork(runtime.id, network.caipId); } } diff --git a/src/background/services/network/handlers/avalanche_setDeveloperMode.ts b/src/background/services/network/handlers/avalanche_setDeveloperMode.ts index a64e039c..c89e7948 100644 --- a/src/background/services/network/handlers/avalanche_setDeveloperMode.ts +++ b/src/background/services/network/handlers/avalanche_setDeveloperMode.ts @@ -7,6 +7,7 @@ import { NetworkService } from '../NetworkService'; import { ethErrors } from 'eth-rpc-errors'; import { openApprovalWindow } from '@src/background/runtime/openApprovalWindow'; import { ChainId } from '@avalabs/core-chains-sdk'; +import { chainIdToCaip } from '@src/utils/caipConversion'; @injectable() export class AvalancheSetDeveloperModeHandler extends DAppRequestHandler { @@ -73,7 +74,11 @@ export class AvalancheSetDeveloperModeHandler extends DAppRequestHandler { await this.networkService.setNetwork( domain, - isTestmode ? ChainId.AVALANCHE_TESTNET_ID : ChainId.AVALANCHE_MAINNET_ID + chainIdToCaip( + isTestmode + ? ChainId.AVALANCHE_TESTNET_ID + : ChainId.AVALANCHE_MAINNET_ID + ) ); onSuccess(null); diff --git a/src/background/services/network/handlers/saveCustomNetwork.ts b/src/background/services/network/handlers/saveCustomNetwork.ts index a1838f7d..aeb56499 100644 --- a/src/background/services/network/handlers/saveCustomNetwork.ts +++ b/src/background/services/network/handlers/saveCustomNetwork.ts @@ -50,7 +50,7 @@ export class SaveCustomNetworkHandler implements HandlerType { }; } - await this.networkService.setNetwork(runtime.id, addedNetwork.chainId); + await this.networkService.setNetwork(runtime.id, addedNetwork.caipId); return { ...request, diff --git a/src/background/services/network/handlers/setActiveNetwork.ts b/src/background/services/network/handlers/setActiveNetwork.ts index c1bad55d..a351326c 100644 --- a/src/background/services/network/handlers/setActiveNetwork.ts +++ b/src/background/services/network/handlers/setActiveNetwork.ts @@ -6,7 +6,6 @@ import { ExtensionRequest } from '@src/background/connections/extensionConnectio import { ExtensionRequestHandler } from '@src/background/connections/models'; import { NetworkService } from '../NetworkService'; -import { caipToChainId } from '@src/utils/caipConversion'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.NETWORK_SET_ACTIVE, @@ -23,7 +22,7 @@ export class SetActiveNetworkHandler implements HandlerType { handle: HandlerType['handle'] = async ({ request }) => { const [scope] = request.params; const [, err] = await resolve( - this.networkService.setNetwork(runtime.id, caipToChainId(scope)) + this.networkService.setNetwork(runtime.id, scope) ); if (err) { diff --git a/src/background/services/network/handlers/setDeveloperMode.ts b/src/background/services/network/handlers/setDeveloperMode.ts index 5db887b7..2cbe146b 100644 --- a/src/background/services/network/handlers/setDeveloperMode.ts +++ b/src/background/services/network/handlers/setDeveloperMode.ts @@ -7,6 +7,7 @@ import { ExtensionRequestHandler } from '@src/background/connections/models'; import { resolve } from '@src/utils/promiseResolver'; import { NetworkService } from '../NetworkService'; +import { chainIdToCaip } from '@src/utils/caipConversion'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.NETWORK_SET_DEVELOPER_MODE, @@ -25,9 +26,11 @@ export class SetDevelopermodeNetworkHandler implements HandlerType { const [, err] = await resolve( this.networkService.setNetwork( runtime.id, - enableDeveloperMode - ? ChainId.AVALANCHE_TESTNET_ID - : ChainId.AVALANCHE_MAINNET_ID + chainIdToCaip( + enableDeveloperMode + ? ChainId.AVALANCHE_TESTNET_ID + : ChainId.AVALANCHE_MAINNET_ID + ) ) ); diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts index 1fffb409..23cbc659 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.test.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.test.ts @@ -568,7 +568,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.setNetwork).toHaveBeenCalledTimes(1); expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( 'core.app', - 43113 + 'eip155:43113' ); }); @@ -785,7 +785,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( mockPendingAction.site?.domain, - mockPendingAction.displayData.network.chainId + mockPendingAction.displayData.network.caipId ); expect(mockNetworkService.saveCustomNetwork).toHaveBeenCalledTimes(1); expect(mockNetworkService.saveCustomNetwork).toHaveBeenCalledWith({ @@ -828,6 +828,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = const network = { ...mockPendingAction.displayData.network, chainId: 43113, + caipId: 'eip155:43113', }; await handler.onActionApproved( @@ -848,7 +849,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = expect(mockNetworkService.saveCustomNetwork).not.toHaveBeenCalled(); expect(mockNetworkService.setNetwork).toHaveBeenCalledWith( mockPendingAction.site?.domain, - network.chainId + network.caipId ); expect(successHandler).toHaveBeenCalledTimes(1); @@ -871,6 +872,7 @@ describe('background/services/network/handlers/wallet_addEthereumChain.ts', () = network: { ...mockPendingAction.displayData.network, chainId: 43113, + caipId: 'eip155:43113', }, options: { requiresGlacierApiKey: false, diff --git a/src/background/services/network/handlers/wallet_addEthereumChain.ts b/src/background/services/network/handlers/wallet_addEthereumChain.ts index fa8c7ee1..15fbcec3 100644 --- a/src/background/services/network/handlers/wallet_addEthereumChain.ts +++ b/src/background/services/network/handlers/wallet_addEthereumChain.ts @@ -188,7 +188,7 @@ export class WalletAddEthereumChainHandler extends DAppRequestHandler { await this.networkService.saveCustomNetwork(network); } - await this.networkService.setNetwork(domain, network.chainId); + await this.networkService.setNetwork(domain, network.caipId); } onActionApproved = async ( diff --git a/src/background/services/network/handlers/wallet_switchEthereumChain.ts b/src/background/services/network/handlers/wallet_switchEthereumChain.ts index bbfd161b..58e79c91 100644 --- a/src/background/services/network/handlers/wallet_switchEthereumChain.ts +++ b/src/background/services/network/handlers/wallet_switchEthereumChain.ts @@ -55,7 +55,7 @@ export class WalletSwitchEthereumChainHandler extends DAppRequestHandler { if (skipApproval) { await this.networkService.setNetwork( request.site.domain, - supportedNetwork.chainId + supportedNetwork.caipId ); return { ...request, result: null }; } @@ -98,7 +98,7 @@ export class WalletSwitchEthereumChainHandler extends DAppRequestHandler { try { await this.networkService.setNetwork( pendingAction.site.domain, - pendingAction.displayData.network.chainId + pendingAction.displayData.network.caipId ); onSuccess(null); diff --git a/src/background/services/onboarding/finalizeOnboarding.ts b/src/background/services/onboarding/finalizeOnboarding.ts index cfc49fc5..c4341418 100644 --- a/src/background/services/onboarding/finalizeOnboarding.ts +++ b/src/background/services/onboarding/finalizeOnboarding.ts @@ -27,7 +27,7 @@ export async function finalizeOnboarding({ await networkService.addFavoriteNetwork(ChainId.ETHEREUM_HOMESTEAD); const cChain = await networkService.getAvalancheNetwork(); - await networkService.setNetwork(runtime.id, cChain.chainId); + await networkService.setNetwork(runtime.id, cChain.caipId); const allAccounts = accountsService.getAccounts(); const addedAccounts = allAccounts.primary[walletId];