diff --git a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts index 336b4515e9b..b32cae5405a 100644 --- a/packages/multichain-account-service/src/MultichainAccountGroup.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountGroup.test.ts @@ -14,7 +14,6 @@ import { MultichainAccountGroup, } from './MultichainAccountGroup'; import { MultichainAccountWallet } from './MultichainAccountWallet'; -import type { BaseBip44AccountProvider } from './providers'; import type { MockAccountProvider } from './tests'; import { MOCK_SNAP_ACCOUNT_2, @@ -23,7 +22,7 @@ import { MOCK_WALLET_1_ENTROPY_SOURCE, MOCK_WALLET_1_EVM_ACCOUNT, MOCK_WALLET_1_SOL_ACCOUNT, - setupNamedAccountProvider, + setupBip44AccountProvider, getMultichainAccountServiceMessenger, getRootMessenger, } from './tests'; @@ -59,7 +58,7 @@ function setup({ providers: MockAccountProvider[]; } { const providers = accounts.map((providerAccounts, idx) => { - return setupNamedAccountProvider({ + return setupBip44AccountProvider({ name: `Provider ${idx + 1}`, accounts: providerAccounts, }); @@ -68,13 +67,13 @@ function setup({ const wallet = new MultichainAccountWallet>({ entropySource: MOCK_WALLET_1_ENTROPY_SOURCE, messenger: getMultichainAccountServiceMessenger(messenger), - providers: providers as unknown as BaseBip44AccountProvider[], + providers, }); const group = new MultichainAccountGroup({ wallet, groupIndex, - providers: providers as unknown as BaseBip44AccountProvider[], + providers, messenger: getMultichainAccountServiceMessenger(messenger), }); diff --git a/packages/multichain-account-service/src/MultichainAccountGroup.ts b/packages/multichain-account-service/src/MultichainAccountGroup.ts index 08d947d4ded..7025ccaa4be 100644 --- a/packages/multichain-account-service/src/MultichainAccountGroup.ts +++ b/packages/multichain-account-service/src/MultichainAccountGroup.ts @@ -18,7 +18,7 @@ import type { ServiceState, StateKeys } from './MultichainAccountService'; import type { MultichainAccountWallet } from './MultichainAccountWallet'; import { isAccountProviderWrapper, - type BaseBip44AccountProvider, + type Bip44AccountProvider, } from './providers'; import type { MultichainAccountServiceMessenger } from './types'; @@ -38,11 +38,17 @@ export class MultichainAccountGroup< readonly #groupIndex: number; - readonly #providers: BaseBip44AccountProvider[]; + readonly #providers: Bip44AccountProvider[]; - readonly #providerToAccounts: Map; + readonly #providerToAccounts: Map< + Bip44AccountProvider, + Account['id'][] + >; - readonly #accountToProvider: Map; + readonly #accountToProvider: Map< + Account['id'], + Bip44AccountProvider + >; readonly #messenger: MultichainAccountServiceMessenger; @@ -58,7 +64,7 @@ export class MultichainAccountGroup< }: { groupIndex: number; wallet: MultichainAccountWallet; - providers: BaseBip44AccountProvider[]; + providers: Bip44AccountProvider[]; messenger: MultichainAccountServiceMessenger; }) { this.#id = toMultichainAccountGroupId(wallet.id, groupIndex); @@ -181,8 +187,7 @@ export class MultichainAccountGroup< // If for some reason we cannot get this account from the provider, it // might means it has been deleted or something, so we just filter it // out. - // We cast here because TS cannot infer the type of the account from the provider - allAccounts.push(account as Account); + allAccounts.push(account); } } } @@ -214,8 +219,7 @@ export class MultichainAccountGroup< return undefined; } - // We cast here because TS cannot infer the type of the account from the provider - return provider.getAccount(id) as Account; + return provider.getAccount(id); } /** @@ -247,7 +251,7 @@ export class MultichainAccountGroup< */ #removeAccountsFromDisabledProvider( accounts: Account['id'][], - provider: BaseBip44AccountProvider, + provider: Bip44AccountProvider, ): void { accounts.forEach((account) => { this.#accountToProvider.delete(account); diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index 540c1571b57..ef2e99ce09d 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -1,5 +1,6 @@ /* eslint-disable jsdoc/require-jsdoc */ +import type { Bip44AccountProvider } from '@metamask/account-api'; import type { Messenger } from '@metamask/base-controller'; import { mnemonicPhraseToBytes } from '@metamask/key-tree'; import type { KeyringAccount } from '@metamask/keyring-api'; @@ -9,7 +10,6 @@ import type { EthKeyring } from '@metamask/keyring-internal-api'; import type { MultichainAccountServiceOptions } from './MultichainAccountService'; import { MultichainAccountService } from './MultichainAccountService'; -import type { NamedAccountProvider } from './providers'; import { AccountProviderWrapper } from './providers/AccountProviderWrapper'; import { EVM_ACCOUNT_PROVIDER_NAME, @@ -36,7 +36,7 @@ import { getMultichainAccountServiceMessenger, getRootMessenger, makeMockAccountProvider, - setupNamedAccountProvider, + setupBip44AccountProvider, } from './tests'; import type { AllowedActions, @@ -77,7 +77,7 @@ type Mocks = { SolAccountProvider: MockAccountProvider; }; -function mockAccountProvider( +function mockAccountProvider( providerClass: new (messenger: MultichainAccountServiceMessenger) => Provider, mocks: MockAccountProvider, accounts: KeyringAccount[], @@ -88,7 +88,7 @@ function mockAccountProvider( return mocks as unknown as Provider; }); - setupNamedAccountProvider({ + setupBip44AccountProvider({ mocks, accounts, index: idx, diff --git a/packages/multichain-account-service/src/MultichainAccountService.ts b/packages/multichain-account-service/src/MultichainAccountService.ts index 2f4663c05de..dad1d568234 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.ts @@ -16,7 +16,7 @@ import { projectLogger as log } from './logger'; import type { MultichainAccountGroup } from './MultichainAccountGroup'; import { MultichainAccountWallet } from './MultichainAccountWallet'; import type { - BaseBip44AccountProvider, + Bip44AccountProvider, EvmAccountProviderConfig, SolAccountProviderConfig, } from './providers'; @@ -35,7 +35,7 @@ export const serviceName = 'MultichainAccountService'; */ export type MultichainAccountServiceOptions = { messenger: MultichainAccountServiceMessenger; - providers?: BaseBip44AccountProvider[]; + providers?: Bip44AccountProvider[]; providerConfigs?: { [EvmAccountProvider.NAME]?: EvmAccountProviderConfig; [SolAccountProvider.NAME]?: SolAccountProviderConfig; @@ -97,7 +97,7 @@ type CreateWalletValidatedParams = export class MultichainAccountService { readonly #messenger: MultichainAccountServiceMessenger; - readonly #providers: BaseBip44AccountProvider[]; + readonly #providers: Bip44AccountProvider[]; readonly #wallets: Map< MultichainAccountWalletId, diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts index f554b10c814..1e7e23dfd55 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts @@ -33,7 +33,7 @@ import { MOCK_WALLET_1_EVM_ACCOUNT, MOCK_WALLET_1_SOL_ACCOUNT, MockAccountBuilder, - setupNamedAccountProvider, + setupBip44AccountProvider, getMultichainAccountServiceMessenger, getRootMessenger, } from './tests'; @@ -71,20 +71,21 @@ function setup({ providers: MockAccountProvider[]; messenger: MultichainAccountServiceMessenger; } { - const providersList = (providers ?? + const providersList = + providers ?? accounts.map((providerAccounts, i) => { - return setupNamedAccountProvider({ + return setupBip44AccountProvider({ name: `Mocked Provider ${i}`, accounts: providerAccounts, index: i, }); - })) as MockAccountProvider[]; + }); const serviceMessenger = getMultichainAccountServiceMessenger(messenger); const wallet = new MultichainAccountWallet>({ entropySource, - providers: providersList as unknown as BaseBip44AccountProvider[], + providers: providersList, messenger: serviceMessenger, }); diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.ts b/packages/multichain-account-service/src/MultichainAccountWallet.ts index 4c299e6098d..735c903f94e 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.ts @@ -28,7 +28,7 @@ import { MultichainAccountGroup, } from './MultichainAccountGroup'; import type { ServiceState, StateKeys } from './MultichainAccountService'; -import { type BaseBip44AccountProvider, EvmAccountProvider } from './providers'; +import { type Bip44AccountProvider, EvmAccountProvider } from './providers'; import type { MultichainAccountServiceMessenger } from './types'; /** @@ -37,7 +37,7 @@ import type { MultichainAccountServiceMessenger } from './types'; type AccountProviderDiscoveryContext< Account extends Bip44Account, > = { - provider: BaseBip44AccountProvider; + provider: Bip44AccountProvider; stopped: boolean; groupIndex: number; accounts: Account[]; @@ -60,7 +60,7 @@ export class MultichainAccountWallet< readonly #id: MultichainAccountWalletId; - readonly #providers: BaseBip44AccountProvider[]; + readonly #providers: Bip44AccountProvider[]; readonly #entropySource: EntropySourceId; @@ -79,7 +79,7 @@ export class MultichainAccountWallet< entropySource, messenger, }: { - providers: BaseBip44AccountProvider[]; + providers: Bip44AccountProvider[]; entropySource: EntropySourceId; messenger: MultichainAccountServiceMessenger; }) { diff --git a/packages/multichain-account-service/src/providers/BaseBip44AccountProvider.ts b/packages/multichain-account-service/src/providers/BaseBip44AccountProvider.ts index 44354ca011c..3d72cea23e2 100644 --- a/packages/multichain-account-service/src/providers/BaseBip44AccountProvider.ts +++ b/packages/multichain-account-service/src/providers/BaseBip44AccountProvider.ts @@ -37,13 +37,42 @@ export function assertAreBip44Accounts( accounts.forEach(assertIsBip44Account); } -export type NamedAccountProvider< +export type Bip44AccountProvider< Account extends Bip44Account = Bip44Account, > = AccountProvider & { + /** + * Get the provider unique name. + * + * @returns The provider name. + */ getName(): string; + + /** + * Add accounts to the provider. + * + * @param accounts - The accounts to add. + */ + addAccounts(accounts: Account['id'][]): void; + + /** + * Remove accounts from the provider + * + * @param accounts - The accounts to remove. + */ + removeAccountsFromList(accounts: Account['id'][]): void; + + /** + * Check if an account is compatible with this provider. + * + * @returns True if account is compatible, false otherwise. + */ + isAccountCompatible(account: Account): boolean; }; -export abstract class BaseBip44AccountProvider implements NamedAccountProvider { +export abstract class BaseBip44AccountProvider< + Account extends Bip44Account = Bip44Account, +> implements Bip44AccountProvider +{ protected readonly messenger: MultichainAccountServiceMessenger; accounts: Bip44Account['id'][] = []; @@ -52,14 +81,12 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { this.messenger = messenger; } - abstract getName(): string; - /** * Add accounts to the provider. * * @param accounts - The accounts to add. */ - addAccounts(accounts: Bip44Account['id'][]): void { + addAccounts(accounts: Account['id'][]): void { this.accounts.push(...accounts); } @@ -68,11 +95,16 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { * * @returns The accounts list. */ - #getAccountIds(): Bip44Account['id'][] { + #getAccountIds(): Account['id'][] { return this.accounts; } - removeAccountsFromList(accounts: Bip44Account['id'][]): void { + /** + * Remove accounts from the provider. + * + * @param accounts - The accounts to remove. + */ + removeAccountsFromList(accounts: Account['id'][]): void { this.accounts = this.accounts.filter( (account) => !accounts.includes(account), ); @@ -83,14 +115,14 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { * * @returns The accounts list. */ - getAccounts(): Bip44Account[] { + getAccounts(): Account[] { const accountsIds = this.#getAccountIds(); const internalAccounts = this.messenger.call( 'AccountsController:getAccounts', accountsIds, ); - // we cast here because we know that the accounts are BIP-44 compatible - return internalAccounts as Bip44Account[]; + // We cast here because we know that the accounts are BIP-44 compatible + return internalAccounts as unknown as Account[]; } /** @@ -100,9 +132,7 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { * @returns The account. * @throws If the account is not found. */ - getAccount( - id: Bip44Account['id'], - ): Bip44Account { + getAccount(id: Account['id']): Account { const found = this.getAccounts().find((account) => account.id === id); if (!found) { @@ -135,7 +165,9 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { return result as CallbackResult; } - abstract isAccountCompatible(account: Bip44Account): boolean; + abstract getName(): string; + + abstract isAccountCompatible(account: Account): boolean; abstract createAccounts({ entropySource, @@ -143,7 +175,7 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { }: { entropySource: EntropySourceId; groupIndex: number; - }): Promise[]>; + }): Promise; abstract discoverAccounts({ entropySource, @@ -151,5 +183,5 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider { }: { entropySource: EntropySourceId; groupIndex: number; - }): Promise[]>; + }): Promise; } diff --git a/packages/multichain-account-service/src/tests/providers.ts b/packages/multichain-account-service/src/tests/providers.ts index bda07090669..59369a9e54f 100644 --- a/packages/multichain-account-service/src/tests/providers.ts +++ b/packages/multichain-account-service/src/tests/providers.ts @@ -14,7 +14,8 @@ export type MockAccountProvider = { createAccounts: jest.Mock; discoverAccounts: jest.Mock; addAccounts: jest.Mock; - isAccountCompatible?: jest.Mock; + removeAccountsFromList: jest.Mock; + isAccountCompatible: jest.Mock; getName: jest.Mock; isEnabled: boolean; isDisabled: jest.Mock; @@ -33,6 +34,7 @@ export function makeMockAccountProvider( createAccounts: jest.fn(), discoverAccounts: jest.fn(), addAccounts: jest.fn(), + removeAccountsFromList: jest.fn(), isAccountCompatible: jest.fn(), getName: jest.fn(), isDisabled: jest.fn(), @@ -41,7 +43,7 @@ export function makeMockAccountProvider( }; } -export function setupNamedAccountProvider({ +export function setupBip44AccountProvider({ name = 'Mocked Provider', accounts, mocks = makeMockAccountProvider(),