@metamask/multichain-account-service
Version:
Service to manage multichain accounts
1 lines • 9.72 kB
Source Map (JSON)
{"version":3,"file":"MultichainAccountGroup.cjs","sourceRoot":"","sources":["../src/MultichainAccountGroup.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountGroupType, select, selectOne } from '@metamask/account-api';\nimport {\n toMultichainAccountGroupId,\n type MultichainAccountGroupId,\n type MultichainAccountGroup as MultichainAccountGroupDefinition,\n} from '@metamask/account-api';\nimport type { Bip44Account } from '@metamask/account-api';\nimport type { AccountSelector } from '@metamask/account-api';\nimport type { AccountProvider } from '@metamask/account-api';\nimport { type KeyringAccount } from '@metamask/keyring-api';\n\nimport type { MultichainAccountWallet } from './MultichainAccountWallet';\n\n/**\n * A multichain account group that holds multiple accounts.\n */\nexport class MultichainAccountGroup<\n Account extends Bip44Account<KeyringAccount>,\n> implements MultichainAccountGroupDefinition<Account>\n{\n readonly #id: MultichainAccountGroupId;\n\n readonly #wallet: MultichainAccountWallet<Account>;\n\n readonly #groupIndex: number;\n\n readonly #providers: AccountProvider<Account>[];\n\n readonly #providerToAccounts: Map<AccountProvider<Account>, Account['id'][]>;\n\n readonly #accountToProvider: Map<Account['id'], AccountProvider<Account>>;\n\n constructor({\n groupIndex,\n wallet,\n providers,\n }: {\n groupIndex: number;\n wallet: MultichainAccountWallet<Account>;\n providers: AccountProvider<Account>[];\n }) {\n this.#id = toMultichainAccountGroupId(wallet.id, groupIndex);\n this.#groupIndex = groupIndex;\n this.#wallet = wallet;\n this.#providers = providers;\n this.#providerToAccounts = new Map();\n this.#accountToProvider = new Map();\n\n this.sync();\n }\n\n /**\n * Force multichain account synchronization.\n *\n * This can be used if account providers got new accounts that the multichain\n * account doesn't know about.\n */\n sync(): void {\n // Clear reverse mapping and re-construct it entirely based on the refreshed\n // list of accounts from each providers.\n this.#accountToProvider.clear();\n\n for (const provider of this.#providers) {\n // Filter account only for that index.\n const accounts = [];\n for (const account of provider.getAccounts()) {\n if (\n account.options.entropy.id === this.wallet.entropySource &&\n account.options.entropy.groupIndex === this.groupIndex\n ) {\n // We only use IDs to always fetch the latest version of accounts.\n accounts.push(account.id);\n }\n }\n this.#providerToAccounts.set(provider, accounts);\n\n // Reverse-mapping for fast indexing.\n for (const id of accounts) {\n this.#accountToProvider.set(id, provider);\n }\n }\n }\n\n /**\n * Gets the multichain account group ID.\n *\n * @returns The multichain account group ID.\n */\n get id(): MultichainAccountGroupId {\n return this.#id;\n }\n\n /**\n * Gets the multichain account group type.\n *\n * @returns The multichain account type.\n */\n get type(): AccountGroupType.MultichainAccount {\n return AccountGroupType.MultichainAccount;\n }\n\n /**\n * Gets the multichain account's wallet reference (parent).\n *\n * @returns The multichain account's wallet.\n */\n get wallet(): MultichainAccountWallet<Account> {\n return this.#wallet;\n }\n\n /**\n * Gets the multichain account group index.\n *\n * @returns The multichain account group index.\n */\n get groupIndex(): number {\n return this.#groupIndex;\n }\n\n /**\n * Checks if there's any underlying accounts for this multichain accounts.\n *\n * @returns True if there's any underlying accounts, false otherwise.\n */\n hasAccounts(): boolean {\n // If there's anything in the reverse-map, it means we have some accounts.\n return this.#accountToProvider.size > 0;\n }\n\n /**\n * Gets the accounts for this multichain account.\n *\n * @returns The accounts.\n */\n getAccounts(): Account[] {\n const allAccounts: Account[] = [];\n\n for (const [provider, accounts] of this.#providerToAccounts.entries()) {\n for (const id of accounts) {\n const account = provider.getAccount(id);\n\n if (account) {\n // If for some reason we cannot get this account from the provider, it\n // might means it has been deleted or something, so we just filter it\n // out.\n allAccounts.push(account);\n }\n }\n }\n\n return allAccounts;\n }\n\n /**\n * Gets the account for a given account ID.\n *\n * @param id - Account ID.\n * @returns The account or undefined if not found.\n */\n getAccount(id: Account['id']): Account | undefined {\n const provider = this.#accountToProvider.get(id);\n\n // If there's nothing in the map, it means we tried to get an account\n // that does not belong to this multichain account.\n if (!provider) {\n return undefined;\n }\n return provider.getAccount(id);\n }\n\n /**\n * Query an account matching the selector.\n *\n * @param selector - Query selector.\n * @returns The account matching the selector or undefined if not matching.\n * @throws If multiple accounts match the selector.\n */\n get(selector: AccountSelector<Account>): Account | undefined {\n return selectOne(this.getAccounts(), selector);\n }\n\n /**\n * Query accounts matching the selector.\n *\n * @param selector - Query selector.\n * @returns The accounts matching the selector.\n */\n select(selector: AccountSelector<Account>): Account[] {\n return select(this.getAccounts(), selector);\n }\n\n /**\n * Align the multichain account group.\n *\n * This will create accounts for providers that don't have any accounts yet.\n */\n async align(): Promise<void> {\n const results = await Promise.allSettled(\n this.#providers.map((provider) => {\n const accounts = this.#providerToAccounts.get(provider);\n if (!accounts || accounts.length === 0) {\n return provider.createAccounts({\n entropySource: this.wallet.entropySource,\n groupIndex: this.groupIndex,\n });\n }\n return Promise.resolve();\n }),\n );\n\n if (results.some((result) => result.status === 'rejected')) {\n console.warn(\n `Failed to fully align multichain account group for entropy ID: ${this.wallet.entropySource} and group index: ${this.groupIndex}, some accounts might be missing`,\n );\n }\n }\n}\n"]}