Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -59,7 +58,7 @@ function setup({
providers: MockAccountProvider[];
} {
const providers = accounts.map((providerAccounts, idx) => {
return setupNamedAccountProvider({
return setupBip44AccountProvider({
name: `Provider ${idx + 1}`,
accounts: providerAccounts,
});
Expand All @@ -68,13 +67,13 @@ function setup({
const wallet = new MultichainAccountWallet<Bip44Account<InternalAccount>>({
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),
});

Expand Down
24 changes: 14 additions & 10 deletions packages/multichain-account-service/src/MultichainAccountGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -38,11 +38,17 @@ export class MultichainAccountGroup<

readonly #groupIndex: number;

readonly #providers: BaseBip44AccountProvider[];
readonly #providers: Bip44AccountProvider<Account>[];

readonly #providerToAccounts: Map<BaseBip44AccountProvider, Account['id'][]>;
readonly #providerToAccounts: Map<
Bip44AccountProvider<Account>,
Account['id'][]
>;

readonly #accountToProvider: Map<Account['id'], BaseBip44AccountProvider>;
readonly #accountToProvider: Map<
Account['id'],
Bip44AccountProvider<Account>
>;

readonly #messenger: MultichainAccountServiceMessenger;

Expand All @@ -58,7 +64,7 @@ export class MultichainAccountGroup<
}: {
groupIndex: number;
wallet: MultichainAccountWallet<Account>;
providers: BaseBip44AccountProvider[];
providers: Bip44AccountProvider<Account>[];
messenger: MultichainAccountServiceMessenger;
}) {
this.#id = toMultichainAccountGroupId(wallet.id, groupIndex);
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -247,7 +251,7 @@ export class MultichainAccountGroup<
*/
#removeAccountsFromDisabledProvider(
accounts: Account['id'][],
provider: BaseBip44AccountProvider,
provider: Bip44AccountProvider<Account>,
): void {
accounts.forEach((account) => {
this.#accountToProvider.delete(account);
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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,
Expand All @@ -36,7 +36,7 @@ import {
getMultichainAccountServiceMessenger,
getRootMessenger,
makeMockAccountProvider,
setupNamedAccountProvider,
setupBip44AccountProvider,
} from './tests';
import type {
AllowedActions,
Expand Down Expand Up @@ -77,7 +77,7 @@ type Mocks = {
SolAccountProvider: MockAccountProvider;
};

function mockAccountProvider<Provider extends NamedAccountProvider>(
function mockAccountProvider<Provider extends Bip44AccountProvider>(
providerClass: new (messenger: MultichainAccountServiceMessenger) => Provider,
mocks: MockAccountProvider,
accounts: KeyringAccount[],
Expand All @@ -88,7 +88,7 @@ function mockAccountProvider<Provider extends NamedAccountProvider>(
return mocks as unknown as Provider;
});

setupNamedAccountProvider({
setupBip44AccountProvider({
mocks,
accounts,
index: idx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -35,7 +35,7 @@ export const serviceName = 'MultichainAccountService';
*/
export type MultichainAccountServiceOptions = {
messenger: MultichainAccountServiceMessenger;
providers?: BaseBip44AccountProvider[];
providers?: Bip44AccountProvider[];
providerConfigs?: {
[EvmAccountProvider.NAME]?: EvmAccountProviderConfig;
[SolAccountProvider.NAME]?: SolAccountProviderConfig;
Expand Down Expand Up @@ -97,7 +97,7 @@ type CreateWalletValidatedParams =
export class MultichainAccountService {
readonly #messenger: MultichainAccountServiceMessenger;

readonly #providers: BaseBip44AccountProvider[];
readonly #providers: Bip44AccountProvider[];

readonly #wallets: Map<
MultichainAccountWalletId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
MOCK_WALLET_1_EVM_ACCOUNT,
MOCK_WALLET_1_SOL_ACCOUNT,
MockAccountBuilder,
setupNamedAccountProvider,
setupBip44AccountProvider,
getMultichainAccountServiceMessenger,
getRootMessenger,
} from './tests';
Expand Down Expand Up @@ -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<Bip44Account<InternalAccount>>({
entropySource,
providers: providersList as unknown as BaseBip44AccountProvider[],
providers: providersList,
messenger: serviceMessenger,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand All @@ -37,7 +37,7 @@ import type { MultichainAccountServiceMessenger } from './types';
type AccountProviderDiscoveryContext<
Account extends Bip44Account<KeyringAccount>,
> = {
provider: BaseBip44AccountProvider;
provider: Bip44AccountProvider<Account>;
stopped: boolean;
groupIndex: number;
accounts: Account[];
Expand All @@ -60,7 +60,7 @@ export class MultichainAccountWallet<

readonly #id: MultichainAccountWalletId;

readonly #providers: BaseBip44AccountProvider[];
readonly #providers: Bip44AccountProvider<Account>[];

readonly #entropySource: EntropySourceId;

Expand All @@ -79,7 +79,7 @@ export class MultichainAccountWallet<
entropySource,
messenger,
}: {
providers: BaseBip44AccountProvider[];
providers: Bip44AccountProvider<Account>[];
entropySource: EntropySourceId;
messenger: MultichainAccountServiceMessenger;
}) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,42 @@ export function assertAreBip44Accounts(
accounts.forEach(assertIsBip44Account);
}

export type NamedAccountProvider<
export type Bip44AccountProvider<
Account extends Bip44Account<KeyringAccount> = Bip44Account<KeyringAccount>,
> = AccountProvider<Account> & {
/**
* 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<KeyringAccount> = Bip44Account<KeyringAccount>,
> implements Bip44AccountProvider<Account>
{
protected readonly messenger: MultichainAccountServiceMessenger;

accounts: Bip44Account<KeyringAccount>['id'][] = [];
Expand All @@ -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<KeyringAccount>['id'][]): void {
addAccounts(accounts: Account['id'][]): void {
this.accounts.push(...accounts);
}

Expand All @@ -68,11 +95,16 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider {
*
* @returns The accounts list.
*/
#getAccountIds(): Bip44Account<KeyringAccount>['id'][] {
#getAccountIds(): Account['id'][] {
return this.accounts;
}

removeAccountsFromList(accounts: Bip44Account<KeyringAccount>['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),
);
Expand All @@ -83,14 +115,14 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider {
*
* @returns The accounts list.
*/
getAccounts(): Bip44Account<KeyringAccount>[] {
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<KeyringAccount>[];
// We cast here because we know that the accounts are BIP-44 compatible
return internalAccounts as unknown as Account[];
}

/**
Expand All @@ -100,9 +132,7 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider {
* @returns The account.
* @throws If the account is not found.
*/
getAccount(
id: Bip44Account<KeyringAccount>['id'],
): Bip44Account<KeyringAccount> {
getAccount(id: Account['id']): Account {
const found = this.getAccounts().find((account) => account.id === id);

if (!found) {
Expand Down Expand Up @@ -135,21 +165,23 @@ export abstract class BaseBip44AccountProvider implements NamedAccountProvider {
return result as CallbackResult;
}

abstract isAccountCompatible(account: Bip44Account<KeyringAccount>): boolean;
abstract getName(): string;

abstract isAccountCompatible(account: Account): boolean;

abstract createAccounts({
entropySource,
groupIndex,
}: {
entropySource: EntropySourceId;
groupIndex: number;
}): Promise<Bip44Account<KeyringAccount>[]>;
}): Promise<Account[]>;

abstract discoverAccounts({
entropySource,
groupIndex,
}: {
entropySource: EntropySourceId;
groupIndex: number;
}): Promise<Bip44Account<KeyringAccount>[]>;
}): Promise<Account[]>;
}
Loading
Loading