UNPKG

@metamask/multichain-account-service

Version:
1 lines 27.1 kB
{"version":3,"file":"SnapAccountProvider.cjs","sourceRoot":"","sources":["../../src/providers/SnapAccountProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertIsBip44Account } from '@metamask/account-api';\nimport type { Bip44Account } from '@metamask/account-api';\nimport type { TraceCallback, TraceRequest } from '@metamask/controller-utils';\nimport type { SnapKeyring as SnapKeyringV2 } from '@metamask/eth-snap-keyring/v2';\nimport {\n EMPTY_CAPABILITIES,\n isSnapKeyring,\n} from '@metamask/eth-snap-keyring/v2';\nimport {\n AccountCreationType,\n assertCreateAccountOptionIsSupported,\n} from '@metamask/keyring-api';\nimport type {\n CreateAccountBip44DeriveIndexOptions,\n CreateAccountBip44DeriveIndexRangeOptions,\n CreateAccountBip44DiscoverOptions,\n CreateAccountOptions,\n EntropySourceId,\n KeyringAccount,\n} from '@metamask/keyring-api';\nimport type { KeyringCapabilities } from '@metamask/keyring-api/v2';\nimport type { KeyringMetadata } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType } from '@metamask/snaps-utils';\nimport type { CaipChainId } from '@metamask/utils';\nimport { Semaphore } from 'async-mutex';\n\nimport {\n toCreateAccountsV2DataTraces,\n traceFallback,\n TraceName,\n} from '../analytics';\nimport { reportError } from '../errors';\nimport { projectLogger as log, WARNING_PREFIX } from '../logger';\nimport type { MultichainAccountServiceMessenger } from '../types';\nimport { BaseBip44AccountProvider } from './BaseBip44AccountProvider';\nimport { createSnapKeyringClient } from './SnapKeyringClient';\nimport type { Sender, SnapKeyringClient } from './SnapKeyringClient';\nimport { withRetry, withTimeout } from './utils';\n\n/**\n * A proxy to the Snap's keyring operations that routes every call through the\n * `KeyringController` mutex (via {@link SnapAccountProvider.#withSnapKeyring}).\n * Callers receive this object from {@link SnapAccountProvider.withSnap} and\n * never interact with the raw keyring or the mutex directly.\n */\nexport type SnapKeyringProxy = {\n createAccounts: SnapKeyringV2['createAccounts'];\n deleteAccount: SnapKeyringV2['deleteAccount'];\n};\n\nexport type SnapAccountProviderConfig = {\n maxConcurrency?: number;\n discovery: {\n enabled?: boolean;\n maxAttempts: number;\n timeoutMs: number;\n backOffMs: number;\n };\n createAccounts: {\n /**\n * Timeout for account creation operations.\n *\n * NOTE: Batching (and thus whether a single call may create multiple\n * accounts) is driven by the Snap's declared capabilities, not this config.\n * The value might have to be adapted when the Snap supports batching.\n */\n timeoutMs: number;\n };\n resyncAccounts?: {\n /**\n * Whether to automatically remove extra Snap accounts when the Snap has\n * more accounts than MetaMask. If `false`, a warning is logged instead.\n * Defaults to `true`.\n */\n autoRemoveExtraSnapAccounts?: boolean;\n };\n};\n\nexport abstract class SnapAccountProvider extends BaseBip44AccountProvider {\n readonly snapId: SnapId;\n\n protected readonly config: SnapAccountProviderConfig;\n\n /**\n * The Snap's keyring capabilities, sourced from `SnapAccountService` (which\n * reads them from the Snap's manifest). Populated the first time the client\n * is resolved; defaults to an empty capability set until then.\n */\n capabilities: KeyringCapabilities = EMPTY_CAPABILITIES;\n\n /**\n * Version-agnostic keyring client, resolved lazily once the Snap is ready and\n * its capabilities are known — see {@link SnapAccountProvider.withSnap}.\n */\n #client?: SnapKeyringClient;\n\n readonly #sender: Sender;\n\n readonly #queue?: Semaphore;\n\n readonly #trace: TraceCallback;\n\n /**\n * Scopes passed to the v1 `discoverAccounts` client method. Only used on the\n * v1 discovery path.\n *\n * TODO: Remove once all Snaps are fully v2 — discovery is then driven by the\n * Snap's own supported scopes via `createAccounts({ bip44:discover })`.\n */\n protected abstract readonly v1DiscoveryScopes: CaipChainId[];\n\n constructor(\n snapId: SnapId,\n messenger: MultichainAccountServiceMessenger,\n config: SnapAccountProviderConfig,\n /* istanbul ignore next */\n trace: TraceCallback = traceFallback,\n ) {\n super(messenger);\n\n this.snapId = snapId;\n this.#sender = this.#createSender(snapId);\n\n const maxConcurrency = config.maxConcurrency ?? Infinity;\n this.config = {\n ...config,\n discovery: {\n ...config.discovery,\n enabled: config.discovery.enabled ?? true,\n },\n maxConcurrency,\n };\n\n // Create semaphore only if concurrency is limited\n if (isFinite(maxConcurrency)) {\n this.#queue = new Semaphore(maxConcurrency);\n }\n\n this.#trace = trace;\n }\n\n /**\n * Ensures that the Snap is ready to be used.\n *\n * Once this resolves, a Snap keyring for {@link snapId} is guaranteed to\n * exist in the `KeyringController`, so subsequent {@link #withSnapKeyring}\n * calls will not fail with \"No keyring matches the selector\".\n *\n * @returns A promise that resolves when the Snap is ready.\n * @throws An error if the Snap could not become ready.\n */\n async ensureReady(): Promise<void> {\n return this.messenger.call('SnapAccountService:ensureReady', this.snapId);\n }\n\n /**\n * Wraps an async operation with concurrency limiting based on maxConcurrency config.\n * If maxConcurrency is Infinity (the default), the operation runs immediately without throttling.\n * Otherwise, it's queued through the semaphore to respect the concurrency limit.\n *\n * @param operation - The async operation to execute.\n * @returns The result of the operation.\n */\n protected async withMaxConcurrency<Result>(\n operation: () => Promise<Result>,\n ): Promise<Result> {\n if (this.#queue) {\n return this.#queue.runExclusive(operation);\n }\n return operation();\n }\n\n protected async trace<ReturnType>(\n request: TraceRequest,\n fn: () => Promise<ReturnType>,\n ): Promise<ReturnType> {\n return this.#trace(request, fn);\n }\n\n #createSender(snapId: string): Sender {\n return {\n send: async (request: JsonRpcRequest): Promise<Json> => {\n const response = await this.messenger.call(\n 'SnapController:handleRequest',\n {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n },\n );\n return response as Json;\n },\n };\n }\n\n /**\n * Whether the Snap supports the v2 keyring protocol, inferred from its\n * declared capabilities (a v2-capable Snap declares BIP-44 capabilities).\n *\n * @returns `true` if the Snap is v2-capable.\n */\n protected isV2(): boolean {\n return Boolean(this.capabilities.bip44);\n }\n\n /**\n * Resolves the version-agnostic keyring client, fetching the Snap's\n * capabilities from `SnapAccountService` on first use and caching both the\n * capabilities and the resulting client.\n *\n * Callers must ensure the Snap is ready (via\n * {@link SnapAccountProvider.ensureReady}) beforehand so that the\n * capabilities are reliably populated — {@link SnapAccountProvider.withSnap}\n * guarantees this ordering.\n *\n * @returns The resolved {@link SnapKeyringClient}.\n */\n async #resolveClient(): Promise<SnapKeyringClient> {\n if (!this.#client) {\n this.capabilities = await this.messenger.call(\n 'SnapAccountService:getCapabilities',\n this.snapId,\n );\n this.#client = createSnapKeyringClient(this.#sender, this.isV2());\n }\n return this.#client;\n }\n\n async resyncAccounts(\n accounts: Bip44Account<InternalAccount>[],\n ): Promise<void> {\n await this.withSnap(async ({ client, keyring }) => {\n const localSnapAccounts = accounts.filter(\n (account) => account.metadata.snap?.id === this.snapId,\n );\n const snapAccounts = new Set(\n (await client.getAccounts()).map((account) => account.id),\n );\n\n // NOTE: This should never happen, but if it does, we recover by deleting the\n // extra accounts from the Snap to bring it back in sync with MetaMask.\n if (localSnapAccounts.length < snapAccounts.size) {\n const autoRemoveExtraSnapAccounts =\n this.config.resyncAccounts?.autoRemoveExtraSnapAccounts ?? true;\n\n if (autoRemoveExtraSnapAccounts) {\n // Build a set of local account IDs for quick lookup\n const localAccountIds = new Set(\n localSnapAccounts.map((account) => account.id),\n );\n\n // Find and delete accounts that exist in Snap but not in MetaMask\n await Promise.all(\n [...snapAccounts].map(async (snapAccountId) => {\n try {\n if (!localAccountIds.has(snapAccountId)) {\n // This account exists in the Snap but not in MetaMask, delete it from\n // the Snap.\n await client.deleteAccount(snapAccountId);\n // Update the local Set so subsequent checks use the correct size\n snapAccounts.delete(snapAccountId);\n }\n } catch (error) {\n reportError(\n this.messenger,\n `Unable to delete de-synced Snap account: ${this.snapId}`,\n error,\n {\n provider: this.getName(),\n snapAccountId,\n },\n );\n }\n }),\n );\n } else {\n const message = `Snap \"${this.snapId}\" has de-synced accounts, Snap has more accounts than MetaMask! (${localSnapAccounts.length} < ${snapAccounts.size})`;\n log(`${WARNING_PREFIX} ${message}`);\n console.warn(message);\n return;\n }\n }\n\n // We want this part to be fast, so we only check for sizes, but we might need\n // to make a real \"diff\" between the 2 states to not miss any de-sync.\n if (localSnapAccounts.length > snapAccounts.size) {\n // We always use the MetaMask list as the main reference here.\n await Promise.all(\n localSnapAccounts.map(async (account) => {\n const { id: entropySource, groupIndex } = account.options.entropy;\n\n try {\n if (!snapAccounts.has(account.id)) {\n // We still need to remove the accounts from the Snap keyring since we're\n // about to create the same account again, which will use a new ID, but will\n // keep using the same address, and the Snap keyring does not allow this.\n await keyring.deleteAccount(account.id);\n // The Snap has no account in its state for this one, we re-create it.\n await this.createAccounts({\n type: AccountCreationType.Bip44DeriveIndex,\n entropySource,\n groupIndex,\n });\n }\n } catch (error) {\n reportError(this.messenger, 'Unable to re-sync accounts', error, {\n provider: this.getName(),\n groupIndex,\n });\n }\n }),\n );\n }\n });\n }\n\n async #withSnapKeyring<CallbackResult = void>(\n operation: ({\n keyring,\n metadata,\n }: {\n keyring: SnapKeyringV2;\n metadata: KeyringMetadata;\n }) => Promise<CallbackResult>,\n ): Promise<CallbackResult> {\n return this.withKeyringV2<SnapKeyringV2, CallbackResult>(\n {\n filter: (keyring) =>\n isSnapKeyring(keyring) && keyring.snapId === this.snapId,\n },\n (args) => operation(args),\n );\n }\n\n protected async withSnap<CallbackResult = void>(\n operation: (snap: {\n client: SnapKeyringClient;\n keyring: SnapKeyringProxy;\n }) => Promise<CallbackResult>,\n ): Promise<CallbackResult> {\n await this.ensureReady();\n const client = await this.#resolveClient();\n const keyring: SnapKeyringProxy = {\n createAccounts: (options) =>\n this.#withSnapKeyring(({ keyring: snapKeyring }) =>\n snapKeyring.createAccounts(options),\n ),\n deleteAccount: (id) =>\n this.#withSnapKeyring(({ keyring: snapKeyring }) =>\n snapKeyring.deleteAccount(id),\n ),\n };\n return await operation({ client, keyring });\n }\n\n abstract isAccountCompatible(account: Bip44Account<InternalAccount>): boolean;\n\n protected toBip44Account(\n account: KeyringAccount,\n _options: { entropySource: EntropySourceId; groupIndex: number },\n ): Bip44Account<KeyringAccount> {\n assertIsBip44Account(account);\n return account;\n }\n\n protected async createBip44Accounts(\n keyring: SnapKeyringProxy,\n options:\n | CreateAccountBip44DeriveIndexOptions\n | CreateAccountBip44DeriveIndexRangeOptions\n | CreateAccountBip44DiscoverOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withMaxConcurrency(async () => {\n const { entropySource } = options;\n\n const snapAccounts = await withTimeout(\n () =>\n this.trace(\n {\n name: TraceName.ProviderCreateAccounts,\n data: {\n provider: this.getName(),\n ...toCreateAccountsV2DataTraces(options),\n },\n },\n () => keyring.createAccounts(options),\n ),\n this.config.createAccounts.timeoutMs,\n );\n\n const groupIndexOffset =\n options.type === `${AccountCreationType.Bip44DeriveIndexRange}`\n ? options.range.from\n : options.groupIndex;\n\n return snapAccounts.map((snapAccount, index) => {\n const groupIndex = groupIndexOffset + index;\n const account = this.toBip44Account(snapAccount, {\n entropySource,\n groupIndex,\n });\n\n this.accounts.add(snapAccount.id);\n return account;\n });\n });\n }\n\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n assertCreateAccountOptionIsSupported(options, [\n `${AccountCreationType.Bip44DeriveIndex}`,\n `${AccountCreationType.Bip44DeriveIndexRange}`,\n ]);\n\n return this.withSnap(async ({ keyring }) =>\n this.createBip44Accounts(keyring, options),\n );\n }\n\n /**\n * Delete a snap account by id.\n *\n * Resolves the account's address from the tracked account, then forwards to\n * the legacy `SnapKeyring.removeAccount(address)`. The Snap keyring takes\n * care of notifying the snap to clean up its own state through the normal\n * account-removal flow (same path used by `resyncAccounts`).\n *\n * @param id - The id of the account to delete.\n */\n async deleteAccount(id: Bip44Account<KeyringAccount>['id']): Promise<void> {\n const account = this.getAccount(id);\n\n await this.#withSnapKeyring(async ({ keyring }) => {\n await keyring.deleteAccount(account.id);\n });\n\n this.accounts.delete(id);\n }\n\n /**\n * Discovers accounts for the given entropy source and group index.\n *\n * v2 Snaps drive discovery through `createAccounts({ bip44:discover })`: the\n * Snap checks for on-chain activity (using its own supported scopes) and\n * returns the created account(s), or nothing once discovery is exhausted.\n *\n * v1 Snaps use the client's `discoverAccounts` to detect activity on\n * {@link v1DiscoveryScopes}, then create the account for the group index.\n *\n * @param options - The discovery options.\n * @param options.entropySource - The entropy source to discover accounts for.\n * @param options.groupIndex - The group index to discover accounts for.\n * @returns The discovered (and created) accounts, or an empty array when\n * there is nothing to discover at this group index.\n */\n async discoverAccounts({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): Promise<Bip44Account<KeyringAccount>[]> {\n return this.withSnap(async ({ client, keyring }) =>\n this.trace(\n {\n name: TraceName.SnapDiscoverAccounts,\n data: {\n provider: this.getName(),\n },\n },\n async () => {\n if (!this.config.discovery.enabled) {\n return [];\n }\n\n if (this.isV2()) {\n // The v2 client has no `discoverAccounts`, so discovery is only\n // possible when the Snap supports `bip44:discover`. Otherwise there\n // is no way to discover and we report nothing.\n if (!this.capabilities.bip44?.discover) {\n return [];\n }\n\n // v2: the Snap detects on-chain activity and creates the account in\n // a single `createAccounts({ bip44:discover })` call. An empty\n // result means discovery is exhausted at this group index.\n return this.createBip44Accounts(keyring, {\n type: AccountCreationType.Bip44Discover,\n entropySource,\n groupIndex,\n });\n }\n\n // v1: detect activity via the client, then create the account for\n // this group index.\n const discoveredAccounts = await withRetry(\n () =>\n withTimeout(\n () =>\n client.discoverAccounts(\n this.v1DiscoveryScopes,\n entropySource,\n groupIndex,\n ),\n this.config.discovery.timeoutMs,\n ),\n {\n maxAttempts: this.config.discovery.maxAttempts,\n backOffMs: this.config.discovery.backOffMs,\n },\n );\n\n if (!discoveredAccounts.length) {\n return [];\n }\n\n return this.createBip44Accounts(keyring, {\n type: AccountCreationType.Bip44DeriveIndex,\n entropySource,\n groupIndex,\n });\n },\n ),\n );\n }\n}\n\nexport const isSnapAccountProvider = (\n provider: unknown,\n): provider is SnapAccountProvider => {\n return provider instanceof SnapAccountProvider;\n};\n"]}