From f406ae321122d081f0ad8857682c33b8c03160f6 Mon Sep 17 00:00:00 2001 From: Viktor Vasas Date: Thu, 19 Sep 2024 20:41:07 +0200 Subject: [PATCH] refactor: types --- .../services/accounts/AccountsService.test.ts | 101 ++- .../accounts/handlers/getPrivateKey.test.ts | 8 +- .../accounts/handlers/getPrivateKey.ts | 9 +- .../fireblocks/FireblocksSecretsService.ts | 13 +- .../fireblocks/FireblocksService.test.ts | 15 +- .../fireblocksUpdateApiCredentials.test.ts | 4 +- ...migrateMissingPublicKeysFromLedger.test.ts | 13 +- .../migrateMissingPublicKeysFromLedger.ts | 11 +- .../services/secrets/SecretsService.test.ts | 610 +++++++++++++++++- .../services/secrets/SecretsService.ts | 6 +- .../services/seedless/SeedlessTokenStorage.ts | 10 +- .../handlers/updateSignerToken.test.ts | 38 +- .../seedless/handlers/updateSignerToken.ts | 11 +- .../services/wallet/WalletService.test.ts | 555 +--------------- .../eth_sendTransaction.test.ts | 3 + .../handlers/getUnencryptedMnemonic.test.ts | 16 +- .../wallet/handlers/getUnencryptedMnemonic.ts | 14 +- .../storeBtcWalletPolicyDetails.test.ts | 21 +- .../handlers/storeBtcWalletPolicyDetails.ts | 14 +- 19 files changed, 819 insertions(+), 653 deletions(-) diff --git a/src/background/services/accounts/AccountsService.test.ts b/src/background/services/accounts/AccountsService.test.ts index 86ddde0ea..bbf19a6c7 100644 --- a/src/background/services/accounts/AccountsService.test.ts +++ b/src/background/services/accounts/AccountsService.test.ts @@ -1,9 +1,7 @@ import { NetworkService } from '../network/NetworkService'; import { AccountsService } from './AccountsService'; import { StorageService } from '../storage/StorageService'; -import { WalletService } from '../wallet/WalletService'; import { LedgerService } from '../ledger/LedgerService'; -import { KeystoneService } from '../keystone/KeystoneService'; import { AccountsEvents, ACCOUNTS_STORAGE_KEY, @@ -15,19 +13,16 @@ import { NetworkVMType } from '@avalabs/core-chains-sdk'; import { WalletConnectStorage } from '../walletConnect/WalletConnectStorage'; import { WalletConnectService } from '../walletConnect/WalletConnectService'; import { PermissionsService } from '../permissions/PermissionsService'; -import { FireblocksService } from '../fireblocks/FireblocksService'; import { SecretsService } from '../secrets/SecretsService'; import { isProductionBuild } from '@src/utils/environment'; import { AnalyticsServicePosthog } from '../analytics/AnalyticsServicePosthog'; import { SecretType } from '../secrets/models'; jest.mock('../storage/StorageService'); -jest.mock('../wallet/WalletService'); +jest.mock('../secrets/SecretsService'); jest.mock('../ledger/LedgerService'); jest.mock('../lock/LockService'); -jest.mock('../keystone/KeystoneService'); jest.mock('../permissions/PermissionsService'); -jest.mock('../fireblocks/FireblocksService'); jest.mock('../analytics/utils/encryptAnalyticsData'); jest.mock('@src/utils/environment'); @@ -40,20 +35,10 @@ describe('background/services/accounts/AccountsService', () => { ); const storageService = new StorageService({} as any); const ledgerService = new LedgerService(); - const keystoneService = new KeystoneService(); const walletConnectService = new WalletConnectService( new WalletConnectStorage(storageService) ); - const fireblocksService = new FireblocksService({} as any); - const secretsProvider = new SecretsService(storageService); - const walletService = new WalletService( - networkService, - ledgerService, - keystoneService, - walletConnectService, - fireblocksService, - secretsProvider - ); + const secretsService = new SecretsService(storageService); const permissionsService = new PermissionsService({} as any); @@ -160,7 +145,7 @@ describe('background/services/accounts/AccountsService', () => { jest.resetAllMocks(); (storageService.load as jest.Mock).mockResolvedValue(emptyAccounts); analyticsServicePosthog.captureEncryptedEvent = jest.fn(); - (walletService.addAddress as jest.Mock).mockResolvedValue({ + (secretsService.addAddress as jest.Mock).mockResolvedValue({ [NetworkVMType.EVM]: evmAddress, [NetworkVMType.BITCOIN]: btcAddress, [NetworkVMType.AVM]: avmAddress, @@ -171,10 +156,12 @@ describe('background/services/accounts/AccountsService', () => { networkService.developerModeChanged.remove = jest.fn(); accountsService = new AccountsService( storageService, - walletService, networkService, permissionsService, - analyticsServicePosthog + analyticsServicePosthog, + secretsService, + ledgerService, + walletConnectService ); }); @@ -257,14 +244,14 @@ describe('background/services/accounts/AccountsService', () => { const mockedAccounts = mockAccounts(true); (storageService.load as jest.Mock).mockResolvedValue(mockAccounts(false)); - (walletService.getAddresses as jest.Mock).mockResolvedValue({ + (secretsService.getAddresses as jest.Mock).mockResolvedValue({ [NetworkVMType.EVM]: evmAddress, [NetworkVMType.BITCOIN]: btcAddress, [NetworkVMType.AVM]: avmAddress, [NetworkVMType.PVM]: pvmAddress, [NetworkVMType.CoreEth]: coreEthAddress, }); - (walletService.getImportedAddresses as jest.Mock) + (secretsService.getImportedAddresses as jest.Mock) .mockResolvedValueOnce({ ...mockedAccounts.imported['0x1'], id: '0x1' }) .mockResolvedValueOnce({ ...mockedAccounts.imported['0x2'], @@ -275,18 +262,18 @@ describe('background/services/accounts/AccountsService', () => { expect(storageService.load).toBeCalledTimes(1); expect(storageService.load).toBeCalledWith(ACCOUNTS_STORAGE_KEY); - expect(walletService.getAddresses).toBeCalledTimes(3); - expect(walletService.getAddresses).toHaveBeenNthCalledWith( + expect(secretsService.getAddresses).toBeCalledTimes(3); + expect(secretsService.getAddresses).toHaveBeenNthCalledWith( 1, 0, walletId ); - expect(walletService.getAddresses).toHaveBeenNthCalledWith( + expect(secretsService.getAddresses).toHaveBeenNthCalledWith( 2, 1, walletId ); - expect(walletService.getImportedAddresses).toBeCalledTimes(3); + expect(secretsService.getImportedAddresses).toBeCalledTimes(3); const accounts = accountsService.getAccounts(); @@ -296,14 +283,14 @@ describe('background/services/accounts/AccountsService', () => { it('account addresses are updated on developer mode change', async () => { const mockedAccounts = mockAccounts(true); (storageService.load as jest.Mock).mockResolvedValue(mockAccounts(true)); - (walletService.getAddresses as jest.Mock).mockResolvedValue({ + (secretsService.getAddresses as jest.Mock).mockResolvedValue({ [NetworkVMType.EVM]: otherEvmAddress, [NetworkVMType.BITCOIN]: otherBtcAddress, [NetworkVMType.AVM]: avmAddress, [NetworkVMType.PVM]: pvmAddress, [NetworkVMType.CoreEth]: coreEthAddress, }); - (walletService.getImportedAddresses as jest.Mock) + (secretsService.getImportedAddresses as jest.Mock) .mockResolvedValueOnce({ ...mockedAccounts.imported['fb-acc'], }) @@ -325,7 +312,7 @@ describe('background/services/accounts/AccountsService', () => { await accountsService.onUnlock(); - expect(walletService.getAddresses).not.toBeCalled(); + expect(secretsService.getAddresses).not.toBeCalled(); const accounts = accountsService.getAccounts(); expect(accounts).toStrictEqual(mockedAccounts); @@ -346,7 +333,7 @@ describe('background/services/accounts/AccountsService', () => { beforeEach(async () => { mockedAccounts = mockAccounts(true); jest.mocked(storageService.load).mockResolvedValue(mockedAccounts); - jest.mocked(walletService.getAddresses).mockResolvedValue({ + jest.mocked(secretsService.getAddresses).mockResolvedValue({ [NetworkVMType.EVM]: otherEvmAddress, [NetworkVMType.BITCOIN]: otherBtcAddress, [NetworkVMType.AVM]: avmAddress, @@ -359,13 +346,13 @@ describe('background/services/accounts/AccountsService', () => { it('correctly updates addresses for selected primary account', async () => { jest - .mocked(walletService.getImportedAddresses) + .mocked(secretsService.getImportedAddresses) .mockImplementation((id) => mockedAccounts.imported[id]); await accountsService.refreshAddressesForAccount( mockedAccounts.primary[walletId][0]?.id as string ); - expect(walletService.getAddresses).toHaveBeenCalledTimes(1); + expect(secretsService.getAddresses).toHaveBeenCalledTimes(1); expect(accountsService.getAccounts().primary[0]).toEqual( mockAccounts(true, true).primary[0] ); @@ -373,7 +360,7 @@ describe('background/services/accounts/AccountsService', () => { it('correctly updates addresses for selected imported account', async () => { jest - .mocked(walletService.getImportedAddresses) + .mocked(secretsService.getImportedAddresses) .mockImplementation((id) => { if (id === 'fb-acc') { return { @@ -387,8 +374,10 @@ describe('background/services/accounts/AccountsService', () => { await accountsService.refreshAddressesForAccount('fb-acc'); - expect(walletService.getImportedAddresses).toHaveBeenCalledWith('fb-acc'); - expect(walletService.getAddresses).toHaveBeenCalledTimes(0); + expect(secretsService.getImportedAddresses).toHaveBeenCalledWith( + 'fb-acc' + ); + expect(secretsService.getAddresses).toHaveBeenCalledTimes(0); expect(accountsService.getAccounts().imported['fb-acc']).toEqual({ ...mockAccounts(true, true).imported['fb-acc'], addressC: 'addressC-new', @@ -405,7 +394,7 @@ describe('background/services/accounts/AccountsService', () => { const mockedAccounts = mockAccounts(true, false, 'fb-acc'); jest.spyOn(accountsService, 'activateAccount'); jest.mocked(storageService.load).mockResolvedValue(mockedAccounts); - jest.mocked(walletService.getAddresses).mockResolvedValue({ + jest.mocked(secretsService.getAddresses).mockResolvedValue({ [NetworkVMType.EVM]: otherEvmAddress, [NetworkVMType.BITCOIN]: otherBtcAddress, [NetworkVMType.AVM]: avmAddress, @@ -413,7 +402,7 @@ describe('background/services/accounts/AccountsService', () => { [NetworkVMType.CoreEth]: coreEthAddress, }); jest - .mocked(walletService.getImportedAddresses) + .mocked(secretsService.getImportedAddresses) .mockResolvedValueOnce({ ...mockedAccounts.imported['0x1'], addressC: otherEvmAddress, @@ -504,8 +493,8 @@ describe('background/services/accounts/AccountsService', () => { name: 'Account name', walletId, }); - expect(walletService.addAddress).toBeCalledTimes(1); - expect(walletService.addAddress).toBeCalledWith(0, WALLET_ID); + expect(secretsService.addAddress).toBeCalledTimes(1); + expect(secretsService.addAddress).toBeCalledWith(0, WALLET_ID); const accounts = accountsService.getAccounts(); expect(accounts).toStrictEqual({ @@ -551,8 +540,8 @@ describe('background/services/accounts/AccountsService', () => { expect(accountsService.getAccounts()).toStrictEqual(mockedAccounts); await accountsService.addPrimaryAccount({ walletId: WALLET_ID }); - expect(walletService.addAddress).toBeCalledTimes(1); - expect(walletService.addAddress).toBeCalledWith(2, WALLET_ID); + expect(secretsService.addAddress).toBeCalledTimes(1); + expect(secretsService.addAddress).toBeCalledWith(2, WALLET_ID); expect(permissionsService.addWhitelistDomains).toBeCalledTimes(1); expect(permissionsService.addWhitelistDomains).toBeCalledWith( '0x000000000' @@ -587,8 +576,8 @@ describe('background/services/accounts/AccountsService', () => { name: 'New Account', walletId: WALLET_ID, }); - expect(walletService.addAddress).toBeCalledTimes(1); - expect(walletService.addAddress).toBeCalledWith(2, WALLET_ID); + expect(secretsService.addAddress).toBeCalledTimes(1); + expect(secretsService.addAddress).toBeCalledWith(2, WALLET_ID); expect(permissionsService.addWhitelistDomains).toBeCalledTimes(1); expect(permissionsService.addWhitelistDomains).toBeCalledWith( '0x000000000' @@ -660,7 +649,7 @@ describe('background/services/accounts/AccountsService', () => { expect(storageService.load).toBeCalledWith(ACCOUNTS_STORAGE_KEY); expect(accountsService.getAccounts()).toStrictEqual(emptyAccounts); - (walletService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ + (secretsService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ account: { ...getAllAddresses(), id: uuidMock, @@ -672,8 +661,8 @@ describe('background/services/accounts/AccountsService', () => { name: 'Account name', options, }); - expect(walletService.addImportedWallet).toBeCalledTimes(1); - expect(walletService.addImportedWallet).toBeCalledWith(options); + expect(secretsService.addImportedWallet).toBeCalledTimes(1); + expect(secretsService.addImportedWallet).toBeCalledWith(options); expect(commitMock).toHaveBeenCalled(); expect(permissionsService.addWhitelistDomains).toBeCalledTimes(1); expect(permissionsService.addWhitelistDomains).toBeCalledWith( @@ -718,7 +707,7 @@ describe('background/services/accounts/AccountsService', () => { expect(storageService.load).toBeCalledWith(ACCOUNTS_STORAGE_KEY); expect(accountsService.getAccounts()).toStrictEqual(mockedAccounts); - (walletService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ + (secretsService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ account: { ...getAllAddresses(true), id: uuidMock, @@ -727,8 +716,8 @@ describe('background/services/accounts/AccountsService', () => { }); await accountsService.addImportedAccount({ options }); - expect(walletService.addImportedWallet).toBeCalledTimes(1); - expect(walletService.addImportedWallet).toBeCalledWith(options); + expect(secretsService.addImportedWallet).toBeCalledTimes(1); + expect(secretsService.addImportedWallet).toBeCalledWith(options); expect(commitMock).toHaveBeenCalled(); expect(permissionsService.addWhitelistDomains).toBeCalledTimes(1); expect(permissionsService.addWhitelistDomains).toBeCalledWith( @@ -767,7 +756,7 @@ describe('background/services/accounts/AccountsService', () => { eventListener ); - (walletService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ + (secretsService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ account: { ...getAllAddresses(true), id: uuidMock, @@ -814,7 +803,7 @@ describe('background/services/accounts/AccountsService', () => { expect(storageService.load).toBeCalledWith(ACCOUNTS_STORAGE_KEY); expect(accountsService.getAccounts()).toStrictEqual(mockedAccounts); - (walletService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ + (secretsService.addImportedWallet as jest.Mock).mockResolvedValueOnce({ account: { ...getAllAddresses(), id: '0x1', @@ -825,8 +814,8 @@ describe('background/services/accounts/AccountsService', () => { expect(await accountsService.addImportedAccount({ options })).toEqual( '0x1' ); - expect(walletService.addImportedWallet).toBeCalledTimes(1); - expect(walletService.addImportedWallet).toBeCalledWith(options); + expect(secretsService.addImportedWallet).toBeCalledTimes(1); + expect(secretsService.addImportedWallet).toBeCalledWith(options); expect(commitMock).not.toHaveBeenCalled(); expect(permissionsService.addWhitelistDomains).not.toHaveBeenCalled(); }); @@ -838,7 +827,7 @@ describe('background/services/accounts/AccountsService', () => { data: 'privateKey', }; - (walletService.addImportedWallet as jest.Mock).mockRejectedValueOnce( + (secretsService.addImportedWallet as jest.Mock).mockRejectedValueOnce( new Error(errorMessage) ); expect(permissionsService.addWhitelistDomains).not.toHaveBeenCalled(); @@ -1020,7 +1009,7 @@ describe('background/services/accounts/AccountsService', () => { expect(result).toStrictEqual(expectedAccounts); expect(eventListener).toHaveBeenCalledTimes(1); expect(eventListener).toHaveBeenCalledWith(expectedAccounts); - expect(walletService.deleteImportedWallets).toHaveBeenCalledWith([ + expect(secretsService.deleteImportedWallets).toHaveBeenCalledWith([ '0x1', '0x2', ]); @@ -1066,7 +1055,7 @@ describe('background/services/accounts/AccountsService', () => { it('should throw an error because a seedles account cannot be deleted', async () => { const mockedAccounts = mockAccounts(true); - (walletService.getWalletType as jest.Mock).mockReturnValue( + (secretsService.getWalletType as jest.Mock).mockReturnValue( SecretType.Seedless ); diff --git a/src/background/services/accounts/handlers/getPrivateKey.test.ts b/src/background/services/accounts/handlers/getPrivateKey.test.ts index 56889c80b..c10287cb9 100644 --- a/src/background/services/accounts/handlers/getPrivateKey.test.ts +++ b/src/background/services/accounts/handlers/getPrivateKey.test.ts @@ -5,6 +5,7 @@ import { LockService } from '../../lock/LockService'; import { SecretType } from '../../secrets/models'; import { getWalletFromMnemonic } from '@avalabs/core-wallets-sdk'; import { buildRpcCall } from '@src/tests/test-utils'; +import { AccountsService } from '../AccountsService'; jest.mock('@avalabs/core-wallets-sdk', () => ({ ...jest.requireActual('@avalabs/core-wallets-sdk'), @@ -20,6 +21,7 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => { const lockServiceMock: jest.Mocked = { verifyPassword: jest.fn(), } as any; + const accountsServiceMock: jest.Mocked = {} as any; const request = { id: '123', @@ -28,7 +30,11 @@ describe('background/services/accounts/handlers/getPrivateKey.ts', () => { } as any; const getHandler = () => - new GetPrivateKeyHandler(sercretServiceMock, lockServiceMock); + new GetPrivateKeyHandler( + sercretServiceMock, + lockServiceMock, + accountsServiceMock + ); beforeEach(() => { jest.resetAllMocks(); diff --git a/src/background/services/accounts/handlers/getPrivateKey.ts b/src/background/services/accounts/handlers/getPrivateKey.ts index 8a8151520..46b6676a1 100644 --- a/src/background/services/accounts/handlers/getPrivateKey.ts +++ b/src/background/services/accounts/handlers/getPrivateKey.ts @@ -7,6 +7,7 @@ import { AccountType, GetPrivateKeyErrorTypes } from '../models'; import { utils } from '@avalabs/avalanchejs'; import { LockService } from '../../lock/LockService'; import { SecretType } from '../../secrets/models'; +import { AccountsService } from '../AccountsService'; interface GetPrivateKeyHandlerParamsProps { type: SecretType.Mnemonic | AccountType.IMPORTED; @@ -27,7 +28,8 @@ export class GetPrivateKeyHandler implements HandlerType { constructor( private secretService: SecretsService, - private lockService: LockService + private lockService: LockService, + private accountsService: AccountsService ) {} handle: HandlerType['handle'] = async ({ request }) => { @@ -85,8 +87,9 @@ export class GetPrivateKeyHandler implements HandlerType { } } - const primaryAccount = - await this.secretService.getPrimaryAccountSecrets(); + const primaryAccount = await this.secretService.getPrimaryAccountSecrets( + this.accountsService.activeAccount + ); if ( !primaryAccount || diff --git a/src/background/services/fireblocks/FireblocksSecretsService.ts b/src/background/services/fireblocks/FireblocksSecretsService.ts index 342bcf611..d6e42b987 100644 --- a/src/background/services/fireblocks/FireblocksSecretsService.ts +++ b/src/background/services/fireblocks/FireblocksSecretsService.ts @@ -9,14 +9,23 @@ import { FireblocksBtcAccessErrorCode, FireblocksSecretsProvider, } from './models'; +import { AccountsService } from '../accounts/AccountsService'; @singleton() export class FireblocksSecretsService implements FireblocksSecretsProvider { - constructor(private secretsService: SecretsService) {} + constructor( + private secretsService: SecretsService, + private accountsService: AccountsService + ) {} async getSecrets(): Promise<{ apiKey: string; privateKey: KeyLike }> { + if (!this.accountsService.activeAccount) { + throw new Error('There is no active account!'); + } // By default thought, we'll get the credentials directly from SecretsService - const secrets = await this.secretsService.getActiveAccountSecrets(); + const secrets = await this.secretsService.getActiveAccountSecrets( + this.accountsService.activeAccount + ); if (secrets.secretType !== SecretType.Fireblocks) { throw new FireblocksBtcAccessError( diff --git a/src/background/services/fireblocks/FireblocksService.test.ts b/src/background/services/fireblocks/FireblocksService.test.ts index 1b67ee16e..e8e842149 100644 --- a/src/background/services/fireblocks/FireblocksService.test.ts +++ b/src/background/services/fireblocks/FireblocksService.test.ts @@ -11,6 +11,7 @@ import sentryCaptureException, { } from '@src/monitoring/sentryCaptureException'; import { CommonError } from '@src/utils/errors'; import { ethErrors } from 'eth-rpc-errors'; +import { AccountsService } from '../accounts/AccountsService'; jest.mock('ethers'); jest.mock('../accounts/AccountsService'); @@ -59,8 +60,20 @@ const mockResponsesByPath = }; describe('src/background/services/fireblocks/FireblocksService', () => { + const accountsService = new AccountsService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any + ); const secretsService = jest.mocked(new SecretsService({} as any)); - const secretsProvider = new FireblocksSecretsService(secretsService); + const secretsProvider = new FireblocksSecretsService( + secretsService, + accountsService + ); let service: FireblocksService; beforeEach(() => { diff --git a/src/background/services/fireblocks/handlers/fireblocksUpdateApiCredentials.test.ts b/src/background/services/fireblocks/handlers/fireblocksUpdateApiCredentials.test.ts index 7e5778d26..c04e8d985 100644 --- a/src/background/services/fireblocks/handlers/fireblocksUpdateApiCredentials.test.ts +++ b/src/background/services/fireblocks/handlers/fireblocksUpdateApiCredentials.test.ts @@ -30,10 +30,12 @@ describe('src/background/services/fireblocks/handlers/fireblocksUpdateApiCredent const networkServiceMock = new NetworkService({} as any, {} as any); const secretsServiceMock = new SecretsService({} as any); const accountServiceMock = new AccountsService( - {} as any, {} as any, networkServiceMock, {} as any, + {} as any, + {} as any, + {} as any, {} as any ); const fireblocksServiceMock = new FireblocksService({} as any); diff --git a/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.test.ts b/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.test.ts index 1703e448b..a7d78936a 100644 --- a/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.test.ts +++ b/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.test.ts @@ -9,6 +9,7 @@ import { SecretsService } from '../../secrets/SecretsService'; import { LedgerTransport } from '../LedgerTransport'; import { MigrateMissingPublicKeysFromLedgerHandler } from './migrateMissingPublicKeysFromLedger'; import { buildRpcCall } from '@src/tests/test-utils'; +import { AccountsService } from '../../accounts/AccountsService'; jest.mock('../../secrets/SecretsService'); jest.mock('@avalabs/core-wallets-sdk'); @@ -19,13 +20,23 @@ describe('src/background/services/ledger/handlers/migrateMissingPublicKeysFromLe id: '123', method: ExtensionRequest.LEDGER_MIGRATE_MISSING_PUBKEYS, } as any; + const accountsService = new AccountsService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any + ); const secretsService = jest.mocked(new SecretsService({} as any)); const ledgerService = {} as any; const handleRequest = async () => { const handler = new MigrateMissingPublicKeysFromLedgerHandler( secretsService, - ledgerService + ledgerService, + accountsService ); return handler.handle(buildRpcCall(request)); }; diff --git a/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.ts b/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.ts index e72c5d5cd..7b0f02bea 100644 --- a/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.ts +++ b/src/background/services/ledger/handlers/migrateMissingPublicKeysFromLedger.ts @@ -11,6 +11,7 @@ import { SecretType } from '../../secrets/models'; import { SecretsService } from '../../secrets/SecretsService'; import { PubKeyType } from '../../wallet/models'; import { LedgerService } from '../LedgerService'; +import { AccountsService } from '../../accounts/AccountsService'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.LEDGER_MIGRATE_MISSING_PUBKEYS, @@ -23,12 +24,18 @@ export class MigrateMissingPublicKeysFromLedgerHandler implements HandlerType { constructor( private secretsService: SecretsService, - private ledgerService: LedgerService + private ledgerService: LedgerService, + private accountsService: AccountsService ) {} handle: HandlerType['handle'] = async ({ request }) => { try { - const secrets = await this.secretsService.getActiveAccountSecrets(); + if (!this.accountsService.activeAccount) { + throw new Error('There is no active account'); + } + const secrets = await this.secretsService.getActiveAccountSecrets( + this.accountsService.activeAccount + ); if ( secrets.secretType !== SecretType.Ledger && secrets.secretType !== SecretType.LedgerLive diff --git a/src/background/services/secrets/SecretsService.test.ts b/src/background/services/secrets/SecretsService.test.ts index 6b5d7465f..51a880342 100644 --- a/src/background/services/secrets/SecretsService.test.ts +++ b/src/background/services/secrets/SecretsService.test.ts @@ -15,6 +15,7 @@ import { PubKeyType, WALLET_STORAGE_KEY } from '../wallet/models'; import { SecretType } from './models'; import { SecretsService } from './SecretsService'; +import { WalletConnectService } from '../walletConnect/WalletConnectService'; jest.mock('../storage/StorageService'); jest.mock('../network/NetworkService'); @@ -31,6 +32,26 @@ jest.mock('tsyringe', () => { }; }); +const walletId = 'wallet-id'; + +const evmAddress = '0x000000000'; +const btcAddress = 'btc000000000'; +const avmAddress = 'X-'; +const pvmAddress = 'P-'; +const coreEthAddress = 'C-'; +const activeAccountData = { + index: 0, + id: 'uuid1', + name: 'Account 1', + type: AccountType.PRIMARY, + walletId, + addressC: evmAddress, + addressBTC: btcAddress, + addressAVM: avmAddress, + addressPVM: pvmAddress, + addressCoreEth: coreEthAddress, +}; + const ACTIVE_WALLET_ID = 'active-wallet-id'; describe('src/background/services/secrets/SecretsService.ts', () => { const storageService = jest.mocked(new StorageService({} as CallbackManager)); @@ -42,6 +63,7 @@ describe('src/background/services/secrets/SecretsService.ts', () => { walletId: ACTIVE_WALLET_ID, } as PrimaryAccount; + const walletConnectService = new WalletConnectService({} as any); let secretsService: SecretsService; let getDefaultFujiProviderMock: jest.Mock; let getAddressMock: jest.Mock; @@ -418,9 +440,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('throws error if no secrets are saved', async () => { storageService.load.mockResolvedValue(null); - await expect(secretsService.getActiveAccountSecrets()).rejects.toThrow( - 'Wallet is not initialized' - ); + await expect( + secretsService.getActiveAccountSecrets(activeAccountData) + ).rejects.toThrow('Wallet is not initialized'); }); describe('when no account is active', () => { @@ -435,7 +457,8 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('should throw an error because there is no active account', async () => { expect( - async () => await secretsService.getActiveAccountSecrets() + async () => + await secretsService.getActiveAccountSecrets(activeAccountData) ).rejects.toThrow(); }); }); @@ -456,14 +479,18 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('attaches the account object to the result', async () => { mockMnemonicWallet(); - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); expect(result.account).toBe(account); }); it('recognizes mnemonic wallets', async () => { const secrets = mockMnemonicWallet(); - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ...rest } = secrets.wallets[0]; expect(result).toEqual({ @@ -475,7 +502,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('recognizes Ledger + BIP44 wallets', async () => { const secrets = mockLedgerWallet(); - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ...rest } = secrets.wallets[0]; @@ -490,7 +519,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('recognizes Ledger + LedgerLive wallets', async () => { const secrets = mockLedgerLiveWallet(); - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ...rest } = secrets.wallets[0]; @@ -504,7 +535,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { it('recognizes Keystone wallets', async () => { const secrets = mockKeystoneWallet(); - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { ...rest } = secrets.wallets[0]; @@ -539,7 +572,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { }); it(`returns the imported account's secrets along with the account`, async () => { - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); expect(result).toEqual({ secret: 'secret', @@ -685,10 +720,10 @@ describe('src/background/services/secrets/SecretsService.ts', () => { }); it('removes specified wallet data from storage', async () => { - const result = await secretsService.deleteImportedWallets([ - 'pkAcc', - 'fbAcc', - ]); + const result = await secretsService.deleteImportedWallets( + ['pkAcc', 'fbAcc'], + walletConnectService + ); expect(result).toEqual({ pkAcc, @@ -776,7 +811,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { }); it(`returns the imported account's secrets along with the account`, async () => { - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); expect(result).toEqual({ addresses: { ...secrets.addresses }, @@ -817,7 +854,9 @@ describe('src/background/services/secrets/SecretsService.ts', () => { }); it(`returns the imported account's secrets along with the account`, async () => { - const result = await secretsService.getActiveAccountSecrets(); + const result = await secretsService.getActiveAccountSecrets( + activeAccountData + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { secretType, ...rest } = secrets; @@ -950,7 +989,8 @@ describe('src/background/services/secrets/SecretsService.ts', () => { masterFingerprint, hmacHex, name, - ACTIVE_WALLET_ID + ACTIVE_WALLET_ID, + activeAccountData ); it('throws if wallet type is not Ledger', async () => { @@ -1063,4 +1103,540 @@ describe('src/background/services/secrets/SecretsService.ts', () => { }); }); }); + /* + describe('addAddress', () => { + const addressesMock = { + addressC: 'addressC', + addressBTC: 'addressBTC', + addressAVM: 'addressAVM', + addressPVM: 'addressPVM', + addressCoreEth: 'addressCoreEth', + }; + let getAddressesSpy: jest.SpyInstance; + + beforeEach(() => { + getAddressesSpy = jest.spyOn(walletService as any, 'getAddresses'); + }); + + it('returns the result of getAddresses', async () => { + mockMnemonicWallet(); + getAddressesSpy.mockReturnValueOnce(addressesMock); + + const result = await walletService.addAddress(1, WALLET_ID); + expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); + expect(result).toStrictEqual(addressesMock); + }); + + describe('ledger', () => { + it('throws if transport is not available', async () => { + mockLedgerLiveWallet({ + pubKeys: [], + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ledgerService.recentTransport = undefined; + + await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( + 'Ledger transport not available' + ); + }); + + it('throws when it fails to get EVM pubkey from ledger', async () => { + const transportMock = {} as LedgerTransport; + mockLedgerLiveWallet({ + pubKeys: [], + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ledgerService.recentTransport = transportMock; + + (getPubKeyFromTransport as jest.Mock).mockReturnValueOnce( + Buffer.from('') + ); + + await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( + 'Failed to get public key from device.' + ); + expect(getPubKeyFromTransport).toHaveBeenCalledWith( + transportMock, + 1, + DerivationPath.LedgerLive + ); + }); + + it('throws when it fails to get X/P pubkey from ledger', async () => { + const transportMock = {} as LedgerTransport; + mockLedgerLiveWallet({ + pubKeys: [], + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ledgerService.recentTransport = transportMock; + + (getPubKeyFromTransport as jest.Mock) + .mockReturnValueOnce(Buffer.from('evm')) + .mockReturnValueOnce(Buffer.from('')); + + await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( + 'Failed to get public key from device.' + ); + expect(getPubKeyFromTransport).toHaveBeenCalledWith( + transportMock, + 1, + DerivationPath.LedgerLive + ); + }); + + it('uses pubkey if index is already known', async () => { + const addressBuffEvm = Buffer.from('0x1'); + const addressBuffXP = Buffer.from('0x2'); + getAddressesSpy.mockReturnValueOnce(addressesMock); + mockLedgerLiveWallet({ + pubKeys: [ + { + evm: addressBuffEvm.toString('hex'), + xp: addressBuffXP.toString('hex'), + }, + ], + }); + + const result = await walletService.addAddress(0, WALLET_ID); + expect(getAddressesSpy).toHaveBeenCalledWith(0, WALLET_ID); + expect(getPubKeyFromTransport).not.toHaveBeenCalled(); + expect(result).toStrictEqual(addressesMock); + expect(secretsService.updateSecrets).not.toHaveBeenCalled(); + }); + + it('gets the addresses correctly', async () => { + const addressBuffEvm = Buffer.from('0x1'); + const addressBuffXP = Buffer.from('0x2'); + const transportMock = {} as LedgerTransport; + getAddressesSpy.mockReturnValueOnce(addressesMock); + mockLedgerLiveWallet({ + pubKeys: [], + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ledgerService.recentTransport = transportMock; + (getPubKeyFromTransport as jest.Mock) + .mockReturnValueOnce(addressBuffEvm) + .mockReturnValueOnce(addressBuffXP); + + const result = await walletService.addAddress(0, WALLET_ID); + expect(getAddressesSpy).toHaveBeenCalledWith(0, WALLET_ID); + expect(result).toStrictEqual(addressesMock); + expect(secretsService.updateSecrets).toHaveBeenCalledWith( + { + pubKeys: [ + { + evm: addressBuffEvm.toString('hex'), + xp: addressBuffXP.toString('hex'), + }, + ], + }, + WALLET_ID + ); + }); + }); + + describe('seedless', () => { + const oldKeys = [{ evm: 'evm', xp: 'xp' }]; + const newKeys = [...oldKeys, { evm: 'evm2', xp: 'xp2' }]; + + describe('when public keys for given account are not known yet', () => { + beforeEach(() => { + mockSeedlessWallet({ + pubKeys: oldKeys, + }); + jest.mocked(SeedlessWallet).mockReturnValue(seedlessWalletMock); + + jest + .spyOn(seedlessWalletMock, 'getPublicKeys') + .mockResolvedValue(newKeys); + + jest + .spyOn(walletService, 'getAddresses') + .mockResolvedValueOnce(addressesMock as any); + }); + + it('calls addAccount on SeedlessWallet', async () => { + const result = await walletService.addAddress(1, WALLET_ID); + + expect(SeedlessWallet).toHaveBeenCalledWith({ + networkService, + sessionStorage: expect.any(SeedlessTokenStorage), + addressPublicKey: { evm: 'evm', xp: 'xp' }, + }); + + expect(seedlessWalletMock.addAccount).toHaveBeenCalledWith(1); + expect(secretsService.updateSecrets).toHaveBeenCalledWith( + { + pubKeys: newKeys, + }, + WALLET_ID + ); + expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); + expect(result).toStrictEqual(addressesMock); + }); + }); + + describe('when the public keys for the new account are known', () => { + beforeEach(() => { + mockSeedlessWallet({ + pubKeys: newKeys, + }); + }); + + it('retrieves the addresses without contacting seedless api', async () => { + const addressBuffEvm = Buffer.from('0x1'); + const addressBuffXP = Buffer.from('0x2'); + + getAddressesSpy.mockReturnValueOnce(addressesMock); + + jest + .mocked(getPubKeyFromTransport) + .mockReturnValueOnce(addressBuffEvm as any) + .mockReturnValueOnce(addressBuffXP as any); + + const result = await walletService.addAddress(1, WALLET_ID); + + expect(SeedlessWallet).not.toHaveBeenCalled(); + expect(secretsService.updateSecrets).not.toHaveBeenCalled(); + + expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); + expect(result).toStrictEqual(addressesMock); + }); + }); + }); + }); + */ + + /* + describe('getAddresses', () => { + const addressesMock = (addressC: string, addressBTC: string) => ({ + [NetworkVMType.EVM]: addressC, + [NetworkVMType.BITCOIN]: addressBTC, + [NetworkVMType.AVM]: 'X-', + [NetworkVMType.PVM]: 'P-', + [NetworkVMType.CoreEth]: 'C-', + }); + + it('throws error if walletId is not provided', async () => { + await expect(walletService.getAddresses(0, '')).rejects.toThrow( + 'Wallet id not provided' + ); + }); + + it('throws if storage is empty', async () => { + mockMnemonicWallet({ secretType: 'unknown' }); + await expect(walletService.getAddresses(0, WALLET_ID)).rejects.toThrow( + 'No public key available' + ); + }); + + it('returns the addresses for xpub', async () => { + mockLedgerWallet(); + (getAddressFromXPub as jest.Mock).mockReturnValueOnce('0x1'); + (getBech32AddressFromXPub as jest.Mock).mockReturnValueOnce('0x2'); + (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); + await expect( + walletService.getAddresses(0, WALLET_ID) + ).resolves.toStrictEqual(addressesMock('0x1', '0x2')); + expect(Avalanche.getAddressPublicKeyFromXpub).toBeCalledWith('xpubXP', 0); + expect(getAddressFromXPub).toHaveBeenCalledWith('xpub', 0); + expect(getBech32AddressFromXPub).toHaveBeenCalledWith( + 'xpub', + 0, + networks.testnet + ); + }); + + it('throws if ledger pubkey is missing from storage', async () => { + mockLedgerLiveWallet({ + pubKeys: [], + }); + (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); + + await expect(walletService.getAddresses(0, WALLET_ID)).rejects.toThrow( + 'Account not added' + ); + }); + + it('returns the addresses for pubKey', async () => { + const pubKeyBuff = Buffer.from('pubKey', 'hex'); + mockLedgerLiveWallet({ + pubKeys: [{ evm: 'pubKey', xp: 'pubKeyXP' }], + }); + (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); + (getEvmAddressFromPubKey as jest.Mock).mockReturnValueOnce('0x1'); + (getBtcAddressFromPubKey as jest.Mock).mockReturnValueOnce('0x2'); + + await expect( + walletService.getAddresses(0, WALLET_ID) + ).resolves.toStrictEqual(addressesMock('0x1', '0x2')); + + expect(getEvmAddressFromPubKey).toHaveBeenCalledWith(pubKeyBuff); + expect(getBtcAddressFromPubKey).toHaveBeenCalledWith( + pubKeyBuff, + networks.testnet + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 1, + expect.any(Buffer), + 'X' + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 2, + expect.any(Buffer), + 'P' + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 3, + expect.any(Buffer), + 'C' + ); + }); + }); + */ + + /* + describe('addImportedWallet', () => { + const pubKeyBuffer = Buffer.from('0x111', 'hex'); + + beforeEach(() => { + (networkService.isMainnet as jest.Mock).mockReturnValue(false); + (getPublicKeyFromPrivateKey as jest.Mock).mockReturnValue(pubKeyBuffer); + (getEvmAddressFromPubKey as jest.Mock).mockReturnValue('0x1'); + (getBtcAddressFromPubKey as jest.Mock).mockReturnValue('0x2'); + }); + + it('saves the secret in storage', async () => { + const uuid = 'some unique id'; + (crypto.randomUUID as jest.Mock).mockReturnValueOnce(uuid); + mockMnemonicWallet({ + imported: {}, + }); + + const result = await walletService.addImportedWallet({ + importType: ImportType.PRIVATE_KEY, + data: 'privateKey', + }); + + expect(result).toStrictEqual({ + account: { + id: uuid, + addressBTC: '0x2', + addressC: '0x1', + addressAVM: 'X-', + addressPVM: 'P-', + addressCoreEth: 'C-', + }, + commit: expect.any(Function), + }); + + // make sure the callback is correct + expect(secretsService.saveImportedWallet).not.toHaveBeenCalled(); + await result.commit(); + expect(secretsService.saveImportedWallet).toHaveBeenCalledWith(uuid, { + secretType: SecretType.PrivateKey, + secret: 'privateKey', + }); + }); + + it('throws if unable to calculate public key', async () => { + (getPublicKeyFromPrivateKey as jest.Mock).mockImplementationOnce(() => { + throw new Error('foo'); + }); + + mockMnemonicWallet({ + imported: {}, + }); + + await expect( + walletService.addImportedWallet({ + importType: ImportType.PRIVATE_KEY, + data: 'privateKey', + }) + ).rejects.toThrow('Error while calculating addresses'); + }); + + it('throws if unable to calculate EVM address', async () => { + (getEvmAddressFromPubKey as jest.Mock).mockImplementationOnce(() => { + throw new Error('foo'); + }); + + mockMnemonicWallet({ + imported: {}, + }); + + await expect( + walletService.addImportedWallet({ + importType: ImportType.PRIVATE_KEY, + data: 'privateKey', + }) + ).rejects.toThrow('Error while calculating addresses'); + }); + + it('throws if unable to calculate BTC address', async () => { + (getBtcAddressFromPubKey as jest.Mock).mockImplementationOnce(() => { + throw new Error('foo'); + }); + + mockMnemonicWallet({ + imported: {}, + }); + + await expect( + walletService.addImportedWallet({ + importType: ImportType.PRIVATE_KEY, + data: 'privateKey', + }) + ).rejects.toThrow('Error while calculating addresses'); + }); + }); + */ + + /* + describe('getImportedAddresses', () => { + const pubKeyBuffer = Buffer.from('0x111', 'hex'); + + beforeEach(() => { + (networkService.isMainnet as jest.Mock).mockReturnValue(false); + (getPublicKeyFromPrivateKey as jest.Mock).mockReturnValue(pubKeyBuffer); + (getEvmAddressFromPubKey as jest.Mock).mockReturnValue('0x1'); + (getBtcAddressFromPubKey as jest.Mock).mockReturnValue('0x2'); + }); + + it('throws if imported account is missing from storage', async () => { + secretsService.getImportedAccountSecrets.mockRejectedValue( + new Error('No secrets found for imported account') + ); + + await expect(walletService.getImportedAddresses('id')).rejects.toThrow( + 'No secrets found for imported account' + ); + }); + + it('throws if importType is not supported', async () => { + secretsService.getImportedAccountSecrets.mockResolvedValue({ + secretType: 'unknown' as any, + secret: 'secret', + }); + + await expect(walletService.getImportedAddresses('id')).rejects.toThrow( + 'Unsupported import type' + ); + }); + + it('throws if addresses are missing', async () => { + secretsService.getImportedAccountSecrets.mockResolvedValue({ + secretType: SecretType.PrivateKey, + secret: 'secret', + }); + (getEvmAddressFromPubKey as jest.Mock).mockReturnValueOnce(''); + (getBtcAddressFromPubKey as jest.Mock).mockReturnValueOnce(''); + + await expect(walletService.getImportedAddresses('id')).rejects.toThrow( + 'Missing address' + ); + }); + + it('returns the addresses for PRIVATE_KEY correctly', async () => { + secretsService.getImportedAccountSecrets.mockResolvedValue({ + secretType: SecretType.PrivateKey, + secret: 'secret', + }); + + const result = await walletService.getImportedAddresses('id'); + + expect(result).toStrictEqual({ + addressBTC: '0x2', + addressC: '0x1', + addressAVM: 'X-', + addressPVM: 'P-', + addressCoreEth: 'C-', + }); + + expect(getPublicKeyFromPrivateKey).toHaveBeenCalledWith('secret'); + expect(getEvmAddressFromPubKey).toHaveBeenCalledWith(pubKeyBuffer); + expect(getBtcAddressFromPubKey).toHaveBeenCalledWith( + pubKeyBuffer, + networks.testnet + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 1, + expect.any(Buffer), + 'X' + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 2, + expect.any(Buffer), + 'P' + ); + expect(getAddressMock).toHaveBeenNthCalledWith( + 3, + expect.any(Buffer), + 'C' + ); + }); + }); + */ + + /* + describe('deleteImportedWallets', () => { + it('deletes the provided ids from storage', async () => { + mockMnemonicWallet({ + imported: { + id1: { + type: ImportType.PRIVATE_KEY, + secret: 'secret1', + }, + id2: { + type: ImportType.PRIVATE_KEY, + secret: 'secret2', + }, + id3: { + type: ImportType.PRIVATE_KEY, + secret: 'secret3', + }, + }, + }); + secretsService.deleteImportedWallets.mockResolvedValue({ + id2: { + secretType: SecretType.PrivateKey, + secret: 'secret2', + }, + id3: { + secretType: SecretType.PrivateKey, + secret: 'secret3', + }, + }); + + await walletService.deleteImportedWallets(['id2', 'id3']); + + expect(secretsService.deleteImportedWallets).toHaveBeenCalledWith([ + 'id2', + 'id3', + ]); + }); + }); + */ + /* + describe('deletePrimaryWallets()', () => { + it('should call the secretsService with the right ids', async () => { + const ids = ['wallet-id', 'wallet-id-2']; + await walletService.deletePrimaryWallets(ids); + expect(secretsService.deletePrimaryWallets).toHaveBeenCalledWith(ids); + }); + }); + describe('getWalletType()', () => { + it('should call the secretsService with the right id', async () => { + const id = 'wallet-id'; + await walletService.getWalletType(id); + expect(secretsService.getWalletAccountsSecretsById).toHaveBeenCalledWith( + id + ); + }); + }); + */ }); diff --git a/src/background/services/secrets/SecretsService.ts b/src/background/services/secrets/SecretsService.ts index 5d0b2e4e6..c8926d487 100644 --- a/src/background/services/secrets/SecretsService.ts +++ b/src/background/services/secrets/SecretsService.ts @@ -169,11 +169,8 @@ export class SecretsService { getActiveWalletSecrets( walletKeys: WalletSecretInStorage, - activeAccount?: Account + activeAccount: Account ) { - if (!activeAccount) { - return null; - } const activeWalletId = isPrimaryAccount(activeAccount) ? activeAccount.walletId : activeAccount?.id; @@ -453,6 +450,7 @@ export class SecretsService { const walletKeys = await this.storageService.load( WALLET_STORAGE_KEY ); + console.log('walletKeys: ', walletKeys); if (!walletKeys && strict) { throw new Error('Wallet is not initialized'); diff --git a/src/background/services/seedless/SeedlessTokenStorage.ts b/src/background/services/seedless/SeedlessTokenStorage.ts index c17360762..1095b447b 100644 --- a/src/background/services/seedless/SeedlessTokenStorage.ts +++ b/src/background/services/seedless/SeedlessTokenStorage.ts @@ -22,12 +22,16 @@ export class SeedlessTokenStorage implements SignerSessionStorage { } async retrieve(): Promise { - const secrets = await this.secretsService.getActiveAccountSecrets(); + const secrets = await this.secretsService.loadSecrets(); - if (secrets.secretType !== SecretType.Seedless) { + const seedlessSecrets = secrets.wallets.find( + (wallet) => wallet.secretType === SecretType.Seedless + ); + + if (!seedlessSecrets) { throw new Error('Incorrect secrets format found'); } - return secrets.seedlessSignerToken; + return seedlessSecrets.seedlessSignerToken; } } diff --git a/src/background/services/seedless/handlers/updateSignerToken.test.ts b/src/background/services/seedless/handlers/updateSignerToken.test.ts index 4251de5ba..318879062 100644 --- a/src/background/services/seedless/handlers/updateSignerToken.test.ts +++ b/src/background/services/seedless/handlers/updateSignerToken.test.ts @@ -3,9 +3,11 @@ import { UpdateSignerTokenHandler } from './updateSignerToken'; import { SecretsService } from '../../secrets/SecretsService'; import { SecretType } from '../../secrets/models'; import { buildRpcCall } from '@src/tests/test-utils'; +import { AccountsService } from '../../accounts/AccountsService'; describe('src/background/services/seedless/handlers/updateSignerToken', () => { let secretsService; + let accountsService; beforeEach(() => { jest.resetAllMocks(); @@ -16,6 +18,15 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { }), updateSecrets: jest.fn().mockResolvedValue('walletId'), } as any); + accountsService = new AccountsService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any + ); }); it('returns error when token is missing', async () => { @@ -23,7 +34,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { hasTokenExpired: true, } as any, - secretsService + secretsService, + accountsService ); const result = await handler.handle( @@ -42,7 +54,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { hasTokenExpired: true, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla' } as any; @@ -63,7 +76,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { hasTokenExpired: true, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -91,7 +105,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { hasTokenExpired: true, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -111,7 +126,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { hasTokenExpired: true, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -135,7 +151,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { updateSignerToken, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -157,7 +174,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { updateSignerToken, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -189,7 +207,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { updateSignerToken, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; @@ -222,7 +241,8 @@ describe('src/background/services/seedless/handlers/updateSignerToken', () => { { updateSignerToken, } as any, - secretsService + secretsService, + accountsService ); const token = { token: 'bla bla bla', session_info: { bla: 'bla' } } as any; diff --git a/src/background/services/seedless/handlers/updateSignerToken.ts b/src/background/services/seedless/handlers/updateSignerToken.ts index 0395785b1..dc9ca3c53 100644 --- a/src/background/services/seedless/handlers/updateSignerToken.ts +++ b/src/background/services/seedless/handlers/updateSignerToken.ts @@ -8,6 +8,7 @@ import { ExtensionRequest } from '@src/background/connections/extensionConnectio import { SeedlessSessionManager } from '../SeedlessSessionManager'; import { SecretsService } from '../../secrets/SecretsService'; import { SecretType } from '../../secrets/models'; +import { AccountsService } from '../../accounts/AccountsService'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.SEEDLESS_UPDATE_SIGNER_TOKEN, @@ -21,7 +22,8 @@ export class UpdateSignerTokenHandler implements HandlerType { constructor( private sessionMgr: SeedlessSessionManager, - private secretsService: SecretsService + private secretsService: SecretsService, + private accountsService: AccountsService ) {} handle: HandlerType['handle'] = async ({ request }) => { @@ -40,8 +42,13 @@ export class UpdateSignerTokenHandler implements HandlerType { if (!userId) { return { ...request, error: 'missing user ID' }; } + if (!this.accountsService.activeAccount) { + return { ...request, error: 'missing active account' }; + } - const secrets = await this.secretsService.getActiveAccountSecrets(); + const secrets = await this.secretsService.getActiveAccountSecrets( + this.accountsService.activeAccount + ); if (secrets.secretType !== SecretType.Seedless) { return { diff --git a/src/background/services/wallet/WalletService.test.ts b/src/background/services/wallet/WalletService.test.ts index bcd07067f..e4ee7e3ab 100644 --- a/src/background/services/wallet/WalletService.test.ts +++ b/src/background/services/wallet/WalletService.test.ts @@ -14,20 +14,12 @@ import { AvalancheTransactionRequest, WalletEvents, } from './models'; -import { - AVALANCHE_XP_TEST_NETWORK, - NetworkVMType, -} from '@avalabs/core-chains-sdk'; +import { AVALANCHE_XP_TEST_NETWORK } from '@avalabs/core-chains-sdk'; import { BitcoinLedgerWallet, BitcoinWallet, BitcoinProvider, DerivationPath, - getPubKeyFromTransport, - getAddressFromXPub, - getBech32AddressFromXPub, - getEvmAddressFromPubKey, - getBtcAddressFromPubKey, getPublicKeyFromPrivateKey, getAddressPublicKeyFromXPub, Avalanche, @@ -50,13 +42,13 @@ import { Action, ActionStatus } from '../actions/models'; import { UnsignedTx } from '@avalabs/avalanchejs'; import { FireblocksService } from '../fireblocks/FireblocksService'; import { SecretsService } from '../secrets/SecretsService'; -import { Account, AccountType, ImportType } from '../accounts/models'; +import { Account, AccountType } from '../accounts/models'; import { SecretType } from '../secrets/models'; -import { networks, Transaction } from 'bitcoinjs-lib'; -import { SeedlessTokenStorage } from '../seedless/SeedlessTokenStorage'; +import { Transaction } from 'bitcoinjs-lib'; import { SeedlessSessionManager } from '../seedless/SeedlessSessionManager'; import { Network } from '../network/models'; import { decorateWithCaipId } from '@src/utils/caipConversion'; +import { AccountsService } from '../accounts/AccountsService'; jest.mock('../network/NetworkService'); jest.mock('../secrets/SecretsService'); @@ -89,6 +81,7 @@ describe('background/services/wallet/WalletService.ts', () => { let walletConnectService: WalletConnectService; let fireblocksService: FireblocksService; let secretsService: jest.Mocked; + let accountsService: AccountsService; const privateKeyMock = '4ae3e293d0161fa90bfbf51028ceb1e51fe70bc6167afe4e0fe0927d86555503'; @@ -250,6 +243,15 @@ describe('background/services/wallet/WalletService.ts', () => { networkService = new NetworkService({} as any, {} as any); ledgerService = new LedgerService(); keystoneService = new KeystoneService(); + accountsService = new AccountsService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any + ); walletConnectService = new WalletConnectService( new WalletConnectStorage({} as any) ); @@ -277,7 +279,8 @@ describe('background/services/wallet/WalletService.ts', () => { keystoneService, walletConnectService, fireblocksService, - secretsService + secretsService, + accountsService ); (networkService.getAvalanceProviderXP as jest.Mock).mockReturnValue( @@ -1267,515 +1270,6 @@ describe('background/services/wallet/WalletService.ts', () => { }); }); - describe('addAddress', () => { - const addressesMock = { - addressC: 'addressC', - addressBTC: 'addressBTC', - addressAVM: 'addressAVM', - addressPVM: 'addressPVM', - addressCoreEth: 'addressCoreEth', - }; - let getAddressesSpy: jest.SpyInstance; - - beforeEach(() => { - getAddressesSpy = jest.spyOn(walletService as any, 'getAddresses'); - }); - - it('returns the result of getAddresses', async () => { - mockMnemonicWallet(); - getAddressesSpy.mockReturnValueOnce(addressesMock); - - const result = await walletService.addAddress(1, WALLET_ID); - expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); - expect(result).toStrictEqual(addressesMock); - }); - - describe('ledger', () => { - it('throws if transport is not available', async () => { - mockLedgerLiveWallet({ - pubKeys: [], - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ledgerService.recentTransport = undefined; - - await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( - 'Ledger transport not available' - ); - }); - - it('throws when it fails to get EVM pubkey from ledger', async () => { - const transportMock = {} as LedgerTransport; - mockLedgerLiveWallet({ - pubKeys: [], - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ledgerService.recentTransport = transportMock; - - (getPubKeyFromTransport as jest.Mock).mockReturnValueOnce( - Buffer.from('') - ); - - await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( - 'Failed to get public key from device.' - ); - expect(getPubKeyFromTransport).toHaveBeenCalledWith( - transportMock, - 1, - DerivationPath.LedgerLive - ); - }); - - it('throws when it fails to get X/P pubkey from ledger', async () => { - const transportMock = {} as LedgerTransport; - mockLedgerLiveWallet({ - pubKeys: [], - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ledgerService.recentTransport = transportMock; - - (getPubKeyFromTransport as jest.Mock) - .mockReturnValueOnce(Buffer.from('evm')) - .mockReturnValueOnce(Buffer.from('')); - - await expect(walletService.addAddress(1, WALLET_ID)).rejects.toThrow( - 'Failed to get public key from device.' - ); - expect(getPubKeyFromTransport).toHaveBeenCalledWith( - transportMock, - 1, - DerivationPath.LedgerLive - ); - }); - - it('uses pubkey if index is already known', async () => { - const addressBuffEvm = Buffer.from('0x1'); - const addressBuffXP = Buffer.from('0x2'); - getAddressesSpy.mockReturnValueOnce(addressesMock); - mockLedgerLiveWallet({ - pubKeys: [ - { - evm: addressBuffEvm.toString('hex'), - xp: addressBuffXP.toString('hex'), - }, - ], - }); - - const result = await walletService.addAddress(0, WALLET_ID); - expect(getAddressesSpy).toHaveBeenCalledWith(0, WALLET_ID); - expect(getPubKeyFromTransport).not.toHaveBeenCalled(); - expect(result).toStrictEqual(addressesMock); - expect(secretsService.updateSecrets).not.toHaveBeenCalled(); - }); - - it('gets the addresses correctly', async () => { - const addressBuffEvm = Buffer.from('0x1'); - const addressBuffXP = Buffer.from('0x2'); - const transportMock = {} as LedgerTransport; - getAddressesSpy.mockReturnValueOnce(addressesMock); - mockLedgerLiveWallet({ - pubKeys: [], - }); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ledgerService.recentTransport = transportMock; - (getPubKeyFromTransport as jest.Mock) - .mockReturnValueOnce(addressBuffEvm) - .mockReturnValueOnce(addressBuffXP); - - const result = await walletService.addAddress(0, WALLET_ID); - expect(getAddressesSpy).toHaveBeenCalledWith(0, WALLET_ID); - expect(result).toStrictEqual(addressesMock); - expect(secretsService.updateSecrets).toHaveBeenCalledWith( - { - pubKeys: [ - { - evm: addressBuffEvm.toString('hex'), - xp: addressBuffXP.toString('hex'), - }, - ], - }, - WALLET_ID - ); - }); - }); - - describe('seedless', () => { - const oldKeys = [{ evm: 'evm', xp: 'xp' }]; - const newKeys = [...oldKeys, { evm: 'evm2', xp: 'xp2' }]; - - describe('when public keys for given account are not known yet', () => { - beforeEach(() => { - mockSeedlessWallet({ - pubKeys: oldKeys, - }); - jest.mocked(SeedlessWallet).mockReturnValue(seedlessWalletMock); - - jest - .spyOn(seedlessWalletMock, 'getPublicKeys') - .mockResolvedValue(newKeys); - - jest - .spyOn(walletService, 'getAddresses') - .mockResolvedValueOnce(addressesMock as any); - }); - - it('calls addAccount on SeedlessWallet', async () => { - const result = await walletService.addAddress(1, WALLET_ID); - - expect(SeedlessWallet).toHaveBeenCalledWith({ - networkService, - sessionStorage: expect.any(SeedlessTokenStorage), - addressPublicKey: { evm: 'evm', xp: 'xp' }, - }); - - expect(seedlessWalletMock.addAccount).toHaveBeenCalledWith(1); - expect(secretsService.updateSecrets).toHaveBeenCalledWith( - { - pubKeys: newKeys, - }, - WALLET_ID - ); - expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); - expect(result).toStrictEqual(addressesMock); - }); - }); - - describe('when the public keys for the new account are known', () => { - beforeEach(() => { - mockSeedlessWallet({ - pubKeys: newKeys, - }); - }); - - it('retrieves the addresses without contacting seedless api', async () => { - const addressBuffEvm = Buffer.from('0x1'); - const addressBuffXP = Buffer.from('0x2'); - - getAddressesSpy.mockReturnValueOnce(addressesMock); - - jest - .mocked(getPubKeyFromTransport) - .mockReturnValueOnce(addressBuffEvm as any) - .mockReturnValueOnce(addressBuffXP as any); - - const result = await walletService.addAddress(1, WALLET_ID); - - expect(SeedlessWallet).not.toHaveBeenCalled(); - expect(secretsService.updateSecrets).not.toHaveBeenCalled(); - - expect(getAddressesSpy).toHaveBeenCalledWith(1, WALLET_ID); - expect(result).toStrictEqual(addressesMock); - }); - }); - }); - }); - - describe('getAddresses', () => { - const addressesMock = (addressC: string, addressBTC: string) => ({ - [NetworkVMType.EVM]: addressC, - [NetworkVMType.BITCOIN]: addressBTC, - [NetworkVMType.AVM]: 'X-', - [NetworkVMType.PVM]: 'P-', - [NetworkVMType.CoreEth]: 'C-', - }); - - it('throws error if walletId is not provided', async () => { - await expect(walletService.getAddresses(0, '')).rejects.toThrow( - 'Wallet id not provided' - ); - }); - - it('throws if storage is empty', async () => { - mockMnemonicWallet({ secretType: 'unknown' }); - await expect(walletService.getAddresses(0, WALLET_ID)).rejects.toThrow( - 'No public key available' - ); - }); - - it('returns the addresses for xpub', async () => { - mockLedgerWallet(); - (getAddressFromXPub as jest.Mock).mockReturnValueOnce('0x1'); - (getBech32AddressFromXPub as jest.Mock).mockReturnValueOnce('0x2'); - (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); - await expect( - walletService.getAddresses(0, WALLET_ID) - ).resolves.toStrictEqual(addressesMock('0x1', '0x2')); - expect(Avalanche.getAddressPublicKeyFromXpub).toBeCalledWith('xpubXP', 0); - expect(getAddressFromXPub).toHaveBeenCalledWith('xpub', 0); - expect(getBech32AddressFromXPub).toHaveBeenCalledWith( - 'xpub', - 0, - networks.testnet - ); - }); - - it('throws if ledger pubkey is missing from storage', async () => { - mockLedgerLiveWallet({ - pubKeys: [], - }); - (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); - - await expect(walletService.getAddresses(0, WALLET_ID)).rejects.toThrow( - 'Account not added' - ); - }); - - it('returns the addresses for pubKey', async () => { - const pubKeyBuff = Buffer.from('pubKey', 'hex'); - mockLedgerLiveWallet({ - pubKeys: [{ evm: 'pubKey', xp: 'pubKeyXP' }], - }); - (networkService.isMainnet as jest.Mock).mockReturnValueOnce(false); - (getEvmAddressFromPubKey as jest.Mock).mockReturnValueOnce('0x1'); - (getBtcAddressFromPubKey as jest.Mock).mockReturnValueOnce('0x2'); - - await expect( - walletService.getAddresses(0, WALLET_ID) - ).resolves.toStrictEqual(addressesMock('0x1', '0x2')); - - expect(getEvmAddressFromPubKey).toHaveBeenCalledWith(pubKeyBuff); - expect(getBtcAddressFromPubKey).toHaveBeenCalledWith( - pubKeyBuff, - networks.testnet - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 1, - expect.any(Buffer), - 'X' - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 2, - expect.any(Buffer), - 'P' - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 3, - expect.any(Buffer), - 'C' - ); - }); - }); - - describe('addImportedWallet', () => { - const pubKeyBuffer = Buffer.from('0x111', 'hex'); - - beforeEach(() => { - (networkService.isMainnet as jest.Mock).mockReturnValue(false); - (getPublicKeyFromPrivateKey as jest.Mock).mockReturnValue(pubKeyBuffer); - (getEvmAddressFromPubKey as jest.Mock).mockReturnValue('0x1'); - (getBtcAddressFromPubKey as jest.Mock).mockReturnValue('0x2'); - }); - - it('saves the secret in storage', async () => { - const uuid = 'some unique id'; - (crypto.randomUUID as jest.Mock).mockReturnValueOnce(uuid); - mockMnemonicWallet({ - imported: {}, - }); - - const result = await walletService.addImportedWallet({ - importType: ImportType.PRIVATE_KEY, - data: 'privateKey', - }); - - expect(result).toStrictEqual({ - account: { - id: uuid, - addressBTC: '0x2', - addressC: '0x1', - addressAVM: 'X-', - addressPVM: 'P-', - addressCoreEth: 'C-', - }, - commit: expect.any(Function), - }); - - // make sure the callback is correct - expect(secretsService.saveImportedWallet).not.toHaveBeenCalled(); - await result.commit(); - expect(secretsService.saveImportedWallet).toHaveBeenCalledWith(uuid, { - secretType: SecretType.PrivateKey, - secret: 'privateKey', - }); - }); - - it('throws if unable to calculate public key', async () => { - (getPublicKeyFromPrivateKey as jest.Mock).mockImplementationOnce(() => { - throw new Error('foo'); - }); - - mockMnemonicWallet({ - imported: {}, - }); - - await expect( - walletService.addImportedWallet({ - importType: ImportType.PRIVATE_KEY, - data: 'privateKey', - }) - ).rejects.toThrow('Error while calculating addresses'); - }); - - it('throws if unable to calculate EVM address', async () => { - (getEvmAddressFromPubKey as jest.Mock).mockImplementationOnce(() => { - throw new Error('foo'); - }); - - mockMnemonicWallet({ - imported: {}, - }); - - await expect( - walletService.addImportedWallet({ - importType: ImportType.PRIVATE_KEY, - data: 'privateKey', - }) - ).rejects.toThrow('Error while calculating addresses'); - }); - - it('throws if unable to calculate BTC address', async () => { - (getBtcAddressFromPubKey as jest.Mock).mockImplementationOnce(() => { - throw new Error('foo'); - }); - - mockMnemonicWallet({ - imported: {}, - }); - - await expect( - walletService.addImportedWallet({ - importType: ImportType.PRIVATE_KEY, - data: 'privateKey', - }) - ).rejects.toThrow('Error while calculating addresses'); - }); - }); - - describe('getImportedAddresses', () => { - const pubKeyBuffer = Buffer.from('0x111', 'hex'); - - beforeEach(() => { - (networkService.isMainnet as jest.Mock).mockReturnValue(false); - (getPublicKeyFromPrivateKey as jest.Mock).mockReturnValue(pubKeyBuffer); - (getEvmAddressFromPubKey as jest.Mock).mockReturnValue('0x1'); - (getBtcAddressFromPubKey as jest.Mock).mockReturnValue('0x2'); - }); - - it('throws if imported account is missing from storage', async () => { - secretsService.getImportedAccountSecrets.mockRejectedValue( - new Error('No secrets found for imported account') - ); - - await expect(walletService.getImportedAddresses('id')).rejects.toThrow( - 'No secrets found for imported account' - ); - }); - - it('throws if importType is not supported', async () => { - secretsService.getImportedAccountSecrets.mockResolvedValue({ - secretType: 'unknown' as any, - secret: 'secret', - }); - - await expect(walletService.getImportedAddresses('id')).rejects.toThrow( - 'Unsupported import type' - ); - }); - - it('throws if addresses are missing', async () => { - secretsService.getImportedAccountSecrets.mockResolvedValue({ - secretType: SecretType.PrivateKey, - secret: 'secret', - }); - (getEvmAddressFromPubKey as jest.Mock).mockReturnValueOnce(''); - (getBtcAddressFromPubKey as jest.Mock).mockReturnValueOnce(''); - - await expect(walletService.getImportedAddresses('id')).rejects.toThrow( - 'Missing address' - ); - }); - - it('returns the addresses for PRIVATE_KEY correctly', async () => { - secretsService.getImportedAccountSecrets.mockResolvedValue({ - secretType: SecretType.PrivateKey, - secret: 'secret', - }); - - const result = await walletService.getImportedAddresses('id'); - - expect(result).toStrictEqual({ - addressBTC: '0x2', - addressC: '0x1', - addressAVM: 'X-', - addressPVM: 'P-', - addressCoreEth: 'C-', - }); - - expect(getPublicKeyFromPrivateKey).toHaveBeenCalledWith('secret'); - expect(getEvmAddressFromPubKey).toHaveBeenCalledWith(pubKeyBuffer); - expect(getBtcAddressFromPubKey).toHaveBeenCalledWith( - pubKeyBuffer, - networks.testnet - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 1, - expect.any(Buffer), - 'X' - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 2, - expect.any(Buffer), - 'P' - ); - expect(getAddressMock).toHaveBeenNthCalledWith( - 3, - expect.any(Buffer), - 'C' - ); - }); - }); - - describe('deleteImportedWallets', () => { - it('deletes the provided ids from storage', async () => { - mockMnemonicWallet({ - imported: { - id1: { - type: ImportType.PRIVATE_KEY, - secret: 'secret1', - }, - id2: { - type: ImportType.PRIVATE_KEY, - secret: 'secret2', - }, - id3: { - type: ImportType.PRIVATE_KEY, - secret: 'secret3', - }, - }, - }); - secretsService.deleteImportedWallets.mockResolvedValue({ - id2: { - secretType: SecretType.PrivateKey, - secret: 'secret2', - }, - id3: { - secretType: SecretType.PrivateKey, - secret: 'secret3', - }, - }); - - await walletService.deleteImportedWallets(['id2', 'id3']); - - expect(secretsService.deleteImportedWallets).toHaveBeenCalledWith([ - 'id2', - 'id3', - ]); - }); - }); - describe('getActiveAccountPublicKey', () => { const evmPub = 'evmPub'; const xpPub = 'xpPub'; @@ -2107,21 +1601,4 @@ describe('background/services/wallet/WalletService.ts', () => { ); }); }); - - describe('deletePrimaryWallets()', () => { - it('should call the secretsService with the right ids', async () => { - const ids = ['wallet-id', 'wallet-id-2']; - await walletService.deletePrimaryWallets(ids); - expect(secretsService.deletePrimaryWallets).toHaveBeenCalledWith(ids); - }); - }); - describe('getWalletType()', () => { - it('should call the secretsService with the right id', async () => { - const id = 'wallet-id'; - await walletService.getWalletType(id); - expect(secretsService.getWalletAccountsSecretsById).toHaveBeenCalledWith( - id - ); - }); - }); }); diff --git a/src/background/services/wallet/handlers/eth_sendTransaction/eth_sendTransaction.test.ts b/src/background/services/wallet/handlers/eth_sendTransaction/eth_sendTransaction.test.ts index 424307e9e..b71683a09 100644 --- a/src/background/services/wallet/handlers/eth_sendTransaction/eth_sendTransaction.test.ts +++ b/src/background/services/wallet/handlers/eth_sendTransaction/eth_sendTransaction.test.ts @@ -162,6 +162,8 @@ describe('background/services/wallet/handlers/eth_sendTransaction/eth_sendTransa {} as any, {} as any, {} as any, + {} as any, + {} as any, {} as any ); @@ -172,6 +174,7 @@ describe('background/services/wallet/handlers/eth_sendTransaction/eth_sendTransa {} as any, {} as any, {} as any, + {} as any, {} as any ); const analyticsServicePosthog = new AnalyticsServicePosthog( diff --git a/src/background/services/wallet/handlers/getUnencryptedMnemonic.test.ts b/src/background/services/wallet/handlers/getUnencryptedMnemonic.test.ts index 5f711af9c..15885951f 100644 --- a/src/background/services/wallet/handlers/getUnencryptedMnemonic.test.ts +++ b/src/background/services/wallet/handlers/getUnencryptedMnemonic.test.ts @@ -3,6 +3,7 @@ import { LockService } from '../../lock/LockService'; import { SecretType } from '../../secrets/models'; import { SecretsService } from '../../secrets/SecretsService'; import { GetUnencryptedMnemonicHandler } from './getUnencryptedMnemonic'; +import { AccountsService } from '../../accounts/AccountsService'; describe('src/background/services/wallet/handlers/getUnencryptedMnemonic.ts', () => { const lockService: jest.Mocked = { @@ -11,9 +12,22 @@ describe('src/background/services/wallet/handlers/getUnencryptedMnemonic.ts', () const secretsService: jest.Mocked = { getActiveAccountSecrets: jest.fn(), } as any; + const accountsService = new AccountsService( + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any + ); const buildHandler = () => - new GetUnencryptedMnemonicHandler(secretsService, lockService); + new GetUnencryptedMnemonicHandler( + secretsService, + lockService, + accountsService + ); it('returns error if password is invalid', async () => { lockService.verifyPassword.mockResolvedValue(false); diff --git a/src/background/services/wallet/handlers/getUnencryptedMnemonic.ts b/src/background/services/wallet/handlers/getUnencryptedMnemonic.ts index 83f6329e3..b45f6d1ef 100644 --- a/src/background/services/wallet/handlers/getUnencryptedMnemonic.ts +++ b/src/background/services/wallet/handlers/getUnencryptedMnemonic.ts @@ -4,6 +4,7 @@ import { injectable } from 'tsyringe'; import { LockService } from '../../lock/LockService'; import { SecretType } from '../../secrets/models'; import { SecretsService } from '../../secrets/SecretsService'; +import { AccountsService } from '../../accounts/AccountsService'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.WALLET_UNENCRYPTED_MNEMONIC, @@ -17,7 +18,8 @@ export class GetUnencryptedMnemonicHandler implements HandlerType { constructor( private secretsService: SecretsService, - private lockService: LockService + private lockService: LockService, + private accountsService: AccountsService ) {} handle: HandlerType['handle'] = async ({ request }) => { const [password] = request.params; @@ -38,7 +40,15 @@ export class GetUnencryptedMnemonicHandler implements HandlerType { }; } - const secrets = await this.secretsService.getActiveAccountSecrets(); + if (!this.accountsService.activeAccount) { + return { + ...request, + error: 'there is no active account', + }; + } + const secrets = await this.secretsService.getActiveAccountSecrets( + this.accountsService.activeAccount + ); if (secrets.secretType !== SecretType.Mnemonic) { return { diff --git a/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.test.ts b/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.test.ts index d4e78ba27..f9bcc3488 100644 --- a/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.test.ts +++ b/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.test.ts @@ -9,6 +9,7 @@ import { AccountWithSecrets, SecretType } from '../../secrets/models'; import { SecretsService } from '../../secrets/SecretsService'; import { StoreBtcWalletPolicyDetails } from './storeBtcWalletPolicyDetails'; import { buildRpcCall } from '@src/tests/test-utils'; +import { AccountsService } from '../../accounts/AccountsService'; jest.mock('@avalabs/core-wallets-sdk'); @@ -28,6 +29,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts isMainnet: () => false, } as any; + const accountsServiceMock = jest.mocked({} as unknown as AccountsService); + beforeEach(() => { jest.resetAllMocks(); }); @@ -39,7 +42,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); await expect(handler.handle(buildRpcCall(request))).rejects.toThrow( @@ -56,7 +60,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts } as AccountWithSecrets); const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); await expect(handler.handle(buildRpcCall(request))).rejects.toThrow( @@ -76,7 +81,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); await expect(handler.handle(buildRpcCall(request))).rejects.toThrow( @@ -99,7 +105,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); const result = await handler.handle(buildRpcCall(request)); @@ -131,7 +138,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); const result = await handler.handle(buildRpcCall(request)); @@ -174,7 +182,8 @@ describe('src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts const handler = new StoreBtcWalletPolicyDetails( secretsServiceMock, - networkServiceMock + networkServiceMock, + accountsServiceMock ); const result = await handler.handle(buildRpcCall(request)); diff --git a/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts b/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts index d1523f67f..028dd6b69 100644 --- a/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts +++ b/src/background/services/wallet/handlers/storeBtcWalletPolicyDetails.ts @@ -10,6 +10,7 @@ import { AccountType } from '../../accounts/models'; import { NetworkService } from '../../network/NetworkService'; import { SecretType } from '../../secrets/models'; import { SecretsService } from '../../secrets/SecretsService'; +import { AccountsService } from '../../accounts/AccountsService'; type HandlerType = ExtensionRequestHandler< ExtensionRequest.WALLET_STORE_BTC_WALLET_POLICY_DETAILS, @@ -23,11 +24,17 @@ export class StoreBtcWalletPolicyDetails implements HandlerType { constructor( private secretsService: SecretsService, - private networkService: NetworkService + private networkService: NetworkService, + private accountsService: AccountsService ) {} handle: HandlerType['handle'] = async ({ request }) => { - const secrets = await this.secretsService.getActiveAccountSecrets(); + if (!this.accountsService.activeAccount) { + throw new Error('there is no active account'); + } + const secrets = await this.secretsService.getActiveAccountSecrets( + this.accountsService.activeAccount + ); if ( secrets.secretType !== SecretType.Ledger && @@ -68,7 +75,8 @@ export class StoreBtcWalletPolicyDetails implements HandlerType { masterFingerPrint, hmacHex, name, - secrets.id + secrets.id, + this.accountsService.activeAccount ); }