From c7f0e7a646550ec1c1186979161d0a9f245cd210 Mon Sep 17 00:00:00 2001 From: iamacook Date: Thu, 28 Sep 2023 15:28:03 +0200 Subject: [PATCH] fix: add remaining tests for `WalletConnectWallet` --- jest.config.cjs | 9 +- .../walletconnect/WalletConnectWallet.test.ts | 173 +++++++++++++++--- .../walletconnect/WalletConnectWallet.ts | 10 +- 3 files changed, 154 insertions(+), 38 deletions(-) diff --git a/jest.config.cjs b/jest.config.cjs index bd350fb152..f59643bd6a 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -16,9 +16,12 @@ const customJestConfig = { testEnvironment: 'jest-environment-jsdom', testEnvironmentOptions: { url: 'http://localhost/balances?safe=rin:0xb3b83bf204C458B461de9B0CD2739DB152b4fa5A' }, globals: { - fetch: global.fetch - } + fetch: global.fetch, + }, } // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async -module.exports = createJestConfig(customJestConfig) +module.exports = async () => ({ + ...(await createJestConfig(customJestConfig)()), + transformIgnorePatterns: ['node_modules/(?!(uint8arrays)/)'], +}) diff --git a/src/services/walletconnect/WalletConnectWallet.test.ts b/src/services/walletconnect/WalletConnectWallet.test.ts index 5aab08a86b..fad7b49a2b 100644 --- a/src/services/walletconnect/WalletConnectWallet.test.ts +++ b/src/services/walletconnect/WalletConnectWallet.test.ts @@ -1,6 +1,6 @@ import { hexZeroPad } from 'ethers/lib/utils' import type { ProposalTypes, SessionTypes, SignClientTypes, Verify } from '@walletconnect/types' -import type { IWeb3Wallet } from '@walletconnect/web3wallet' +import type { IWeb3Wallet, Web3WalletTypes } from '@walletconnect/web3wallet' import WalletConnectWallet from './WalletConnectWallet' @@ -22,37 +22,25 @@ jest.mock('@walletconnect/web3wallet', () => { }, } as unknown as IWeb3Wallet['core'] - on = jest.fn() - - off = jest.fn() - + approveSession = jest.fn() + updateSession = jest.fn() disconnectSession = jest.fn() getActiveSessions = jest.fn() respondSessionRequest = jest.fn() - emitSessionEvent = jest.fn() - - updateSession = jest.fn() - - approveSession = jest.fn() - events = { emit: jest.fn(), } as unknown as IWeb3Wallet['events'] - } + on = jest.fn() + off = jest.fn() - return { - Web3Wallet: MockWeb3Wallet, + emitSessionEvent = jest.fn() } -}) -jest.mock('@walletconnect/utils', () => { - // TODO: Import actual utils from @walletconnect/utils in order to complete todo tests return { - getSdkError: jest.fn(() => ({ message: 'error' })), - buildApprovedNamespaces: jest.fn(() => ({})), + Web3Wallet: MockWeb3Wallet, } }) @@ -110,10 +98,89 @@ describe('WalletConnectWallet', () => { }) describe('approveSession', () => { - it.todo('should approve the session with the correct namespace') + it('should approve the session with proposed required/optional chains/methods and required events', async () => { + const approveSessionSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'approveSession') + + const proposal = { + id: 123, + params: { + id: 456, + pairingTopic: 'pairingTopic', + expiry: 789, + requiredNamespaces: { + eip155: { + chains: ['eip155:1'], + methods: ['eth_sendTransaction', 'personal_sign'], + events: ['chainChanged', 'accountsChanged'], + }, + }, + optionalNamespaces: { + eip155: { + chains: ['eip155:43114', 'eip155:42161', 'eip155:8453', 'eip155:100', 'eip155:137', 'eip155:1101'], + // Not included as optional + methods: [ + 'eth_sendTransaction', + 'personal_sign', + 'eth_accounts', + 'eth_requestAccounts', + 'eth_sendRawTransaction', + 'eth_sign', + 'eth_signTransaction', + 'eth_signTypedData', + 'eth_signTypedData_v3', + 'eth_signTypedData_v4', + 'wallet_switchEthereumChain', + 'wallet_addEthereumChain', + 'wallet_getPermissions', + 'wallet_requestPermissions', + 'wallet_registerOnboarding', + 'wallet_watchAsset', + 'wallet_scanQRCode', + ], + events: ['chainChanged', 'accountsChanged', 'message', 'disconnect', 'connect'], + }, + }, + }, + } as unknown as Web3WalletTypes.SessionProposal + + await wallet.approveSession( + proposal, + '69420', // Not in proposal, therefore not supported + hexZeroPad('0x123', 20), + ) + + const namespaces = { + eip155: { + chains: [ + 'eip155:1', + 'eip155:43114', + 'eip155:42161', + 'eip155:8453', + 'eip155:100', + 'eip155:137', + 'eip155:1101', + ], + methods: ['eth_sendTransaction', 'personal_sign'], + events: ['chainChanged', 'accountsChanged'], + accounts: [ + `eip155:1:${hexZeroPad('0x123', 20)}`, + `eip155:43114:${hexZeroPad('0x123', 20)}`, + `eip155:42161:${hexZeroPad('0x123', 20)}`, + `eip155:8453:${hexZeroPad('0x123', 20)}`, + `eip155:100:${hexZeroPad('0x123', 20)}`, + `eip155:137:${hexZeroPad('0x123', 20)}`, + `eip155:1101:${hexZeroPad('0x123', 20)}`, + ], + }, + } + + expect(approveSessionSpy).toHaveBeenCalledWith({ + id: 123, + namespaces, + }) + }) it('should call emitSessionEvent with the correct parameters', async () => { - const approveSessionSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'approveSession') const emitSpy = jest.spyOn(((wallet as any).web3Wallet as IWeb3Wallet).events, 'emit') await wallet.approveSession( @@ -136,21 +203,68 @@ describe('WalletConnectWallet', () => { hexZeroPad('0x123', 20), ) - expect(approveSessionSpy).toHaveBeenCalled() - expect(emitSpy).toHaveBeenCalledWith('session_add') }) }) describe('updateSession', () => { - it.todo('should update the session with the correct namespace') + it('should update the session with the correct namespace', async () => { + const updateSessionSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'updateSession') + const emitSessionEventSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'emitSessionEvent') + + const session = { + topic: 'topic1', + namespaces: { + eip155: { + chains: ['eip155:1'], + accounts: [`eip155:1:${hexZeroPad('0x123', 20)}`], + events: ['chainChanged', 'accountsChanged'], + methods: [], + }, + }, + } as unknown as SessionTypes.Struct - it.todo('should not update the session if the chainId or account is already in the namespace') + await (wallet as any).updateSession(session, '69420', hexZeroPad('0x123', 20)) - it('should call emitSessionEvent with the correct parameters', async () => { + expect(updateSessionSpy).toHaveBeenCalledWith({ + topic: 'topic1', + namespaces: { + eip155: { + chains: ['eip155:69420', 'eip155:1'], + accounts: [`eip155:69420:${hexZeroPad('0x123', 20)}`, `eip155:1:${hexZeroPad('0x123', 20)}`], + events: ['chainChanged', 'accountsChanged'], + methods: [], + }, + }, + }) + + expect(emitSessionEventSpy).toHaveBeenCalledTimes(2) + }) + + it('should not update the session if the chainId and account is already in the namespace', async () => { const updateSessionSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'updateSession') const emitSessionEventSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'emitSessionEvent') + const session = { + topic: 'topic1', + namespaces: { + eip155: { + chains: ['eip155:1'], + accounts: [`eip155:1:${hexZeroPad('0x123', 20)}`], + }, + }, + } as unknown as SessionTypes.Struct + + await (wallet as any).updateSession(session, '1', hexZeroPad('0x123', 20)) + + expect(updateSessionSpy).not.toHaveBeenCalled() + + expect(emitSessionEventSpy).toHaveBeenCalledTimes(2) + }) + + it('should call emitSessionEvent with the correct parameters', async () => { + const emitSessionEventSpy = jest.spyOn((wallet as any).web3Wallet as IWeb3Wallet, 'emitSessionEvent') + await (wallet as any).updateSession( { topic: 'topic1', @@ -160,8 +274,6 @@ describe('WalletConnectWallet', () => { hexZeroPad('0x123', 20), ) - expect(updateSessionSpy).toHaveBeenCalled() - expect(emitSessionEventSpy).toHaveBeenCalledWith({ topic: 'topic1', event: { @@ -260,7 +372,10 @@ describe('WalletConnectWallet', () => { expect(disconnectSessionSpy).toHaveBeenCalledWith({ topic: 'topic1', - reason: expect.any(Object), + reason: { + code: 6000, + message: 'User disconnected.', + }, }) }) }) diff --git a/src/services/walletconnect/WalletConnectWallet.ts b/src/services/walletconnect/WalletConnectWallet.ts index ec12600bde..a7c1e0d43e 100644 --- a/src/services/walletconnect/WalletConnectWallet.ts +++ b/src/services/walletconnect/WalletConnectWallet.ts @@ -89,7 +89,7 @@ class WalletConnectWallet { const eip155ChainIds = chainIds.map(getEip155ChainId) // Create a list of addresses for each chainId - const eip155Accounts = chainIds.map((chainId) => `${getEip155ChainId(chainId)}:${safeAddress}`) + const eip155Accounts = eip155ChainIds.map((eip155ChainId) => `${eip155ChainId}:${safeAddress}`) return buildApprovedNamespaces({ proposal: proposal.params, @@ -98,6 +98,7 @@ class WalletConnectWallet { chains: eip155ChainIds, methods, accounts: eip155Accounts, + // Don't include optionalNamespaces events events: proposal.params.requiredNamespaces[EIP155]?.events || [], }, }, @@ -137,14 +138,11 @@ class WalletConnectWallet { // Add new chainId and/or account to the session namespace if (hasNewChainId || hasNewAccount) { - const uniqueEip155ChainIds = [...new Set([newEip155ChainId, ...currentEip155ChainIds])] - const unqiueEip155Accounts = [...new Set([newEip155Account, ...currentEip155Accounts])] - const namespaces: SessionTypes.Namespaces = { [EIP155]: { ...session.namespaces[EIP155], - chains: uniqueEip155ChainIds, - accounts: unqiueEip155Accounts, + chains: [newEip155ChainId, ...currentEip155ChainIds], + accounts: [newEip155Account, ...currentEip155Accounts], }, }