@metamask/multichain-account-service
Version:
Service to manage multichain accounts
141 lines • 7.04 kB
text/typescript
import type { Bip44Account } from "@metamask/account-api";
import type { TraceCallback, TraceRequest } from "@metamask/controller-utils";
import type { SnapKeyring as SnapKeyringV2 } from "@metamask/eth-snap-keyring/v2";
import type { CreateAccountBip44DeriveIndexOptions, CreateAccountBip44DeriveIndexRangeOptions, CreateAccountBip44DiscoverOptions, CreateAccountOptions, EntropySourceId, KeyringAccount } from "@metamask/keyring-api";
import type { KeyringCapabilities } from "@metamask/keyring-api/v2";
import type { InternalAccount } from "@metamask/keyring-internal-api";
import type { SnapId } from "@metamask/snaps-sdk";
import type { CaipChainId } from "@metamask/utils";
import type { MultichainAccountServiceMessenger } from "../types.mjs";
import { BaseBip44AccountProvider } from "./BaseBip44AccountProvider.mjs";
import type { SnapKeyringClient } from "./SnapKeyringClient.mjs";
/**
* A proxy to the Snap's keyring operations that routes every call through the
* `KeyringController` mutex (via {@link SnapAccountProvider.#withSnapKeyring}).
* Callers receive this object from {@link SnapAccountProvider.withSnap} and
* never interact with the raw keyring or the mutex directly.
*/
export type SnapKeyringProxy = {
createAccounts: SnapKeyringV2['createAccounts'];
deleteAccount: SnapKeyringV2['deleteAccount'];
};
export type SnapAccountProviderConfig = {
maxConcurrency?: number;
discovery: {
enabled?: boolean;
maxAttempts: number;
timeoutMs: number;
backOffMs: number;
};
createAccounts: {
/**
* Timeout for account creation operations.
*
* NOTE: Batching (and thus whether a single call may create multiple
* accounts) is driven by the Snap's declared capabilities, not this config.
* The value might have to be adapted when the Snap supports batching.
*/
timeoutMs: number;
};
resyncAccounts?: {
/**
* Whether to automatically remove extra Snap accounts when the Snap has
* more accounts than MetaMask. If `false`, a warning is logged instead.
* Defaults to `true`.
*/
autoRemoveExtraSnapAccounts?: boolean;
};
};
export declare abstract class SnapAccountProvider extends BaseBip44AccountProvider {
#private;
readonly snapId: SnapId;
protected readonly config: SnapAccountProviderConfig;
/**
* The Snap's keyring capabilities, sourced from `SnapAccountService` (which
* reads them from the Snap's manifest). Populated the first time the client
* is resolved; defaults to an empty capability set until then.
*/
capabilities: KeyringCapabilities;
/**
* Scopes passed to the v1 `discoverAccounts` client method. Only used on the
* v1 discovery path.
*
* TODO: Remove once all Snaps are fully v2 — discovery is then driven by the
* Snap's own supported scopes via `createAccounts({ bip44:discover })`.
*/
protected abstract readonly v1DiscoveryScopes: CaipChainId[];
constructor(snapId: SnapId, messenger: MultichainAccountServiceMessenger, config: SnapAccountProviderConfig, trace?: TraceCallback);
/**
* Ensures that the Snap is ready to be used.
*
* Once this resolves, a Snap keyring for {@link snapId} is guaranteed to
* exist in the `KeyringController`, so subsequent {@link #withSnapKeyring}
* calls will not fail with "No keyring matches the selector".
*
* @returns A promise that resolves when the Snap is ready.
* @throws An error if the Snap could not become ready.
*/
ensureReady(): Promise<void>;
/**
* Wraps an async operation with concurrency limiting based on maxConcurrency config.
* If maxConcurrency is Infinity (the default), the operation runs immediately without throttling.
* Otherwise, it's queued through the semaphore to respect the concurrency limit.
*
* @param operation - The async operation to execute.
* @returns The result of the operation.
*/
protected withMaxConcurrency<Result>(operation: () => Promise<Result>): Promise<Result>;
protected trace<ReturnType>(request: TraceRequest, fn: () => Promise<ReturnType>): Promise<ReturnType>;
/**
* Whether the Snap supports the v2 keyring protocol, inferred from its
* declared capabilities (a v2-capable Snap declares BIP-44 capabilities).
*
* @returns `true` if the Snap is v2-capable.
*/
protected isV2(): boolean;
resyncAccounts(accounts: Bip44Account<InternalAccount>[]): Promise<void>;
protected withSnap<CallbackResult = void>(operation: (snap: {
client: SnapKeyringClient;
keyring: SnapKeyringProxy;
}) => Promise<CallbackResult>): Promise<CallbackResult>;
abstract isAccountCompatible(account: Bip44Account<InternalAccount>): boolean;
protected toBip44Account(account: KeyringAccount, _options: {
entropySource: EntropySourceId;
groupIndex: number;
}): Bip44Account<KeyringAccount>;
protected createBip44Accounts(keyring: SnapKeyringProxy, options: CreateAccountBip44DeriveIndexOptions | CreateAccountBip44DeriveIndexRangeOptions | CreateAccountBip44DiscoverOptions): Promise<Bip44Account<KeyringAccount>[]>;
createAccounts(options: CreateAccountOptions): Promise<Bip44Account<KeyringAccount>[]>;
/**
* Delete a snap account by id.
*
* Resolves the account's address from the tracked account, then forwards to
* the legacy `SnapKeyring.removeAccount(address)`. The Snap keyring takes
* care of notifying the snap to clean up its own state through the normal
* account-removal flow (same path used by `resyncAccounts`).
*
* @param id - The id of the account to delete.
*/
deleteAccount(id: Bip44Account<KeyringAccount>['id']): Promise<void>;
/**
* Discovers accounts for the given entropy source and group index.
*
* v2 Snaps drive discovery through `createAccounts({ bip44:discover })`: the
* Snap checks for on-chain activity (using its own supported scopes) and
* returns the created account(s), or nothing once discovery is exhausted.
*
* v1 Snaps use the client's `discoverAccounts` to detect activity on
* {@link v1DiscoveryScopes}, then create the account for the group index.
*
* @param options - The discovery options.
* @param options.entropySource - The entropy source to discover accounts for.
* @param options.groupIndex - The group index to discover accounts for.
* @returns The discovered (and created) accounts, or an empty array when
* there is nothing to discover at this group index.
*/
discoverAccounts({ entropySource, groupIndex, }: {
entropySource: EntropySourceId;
groupIndex: number;
}): Promise<Bip44Account<KeyringAccount>[]>;
}
export declare const isSnapAccountProvider: (provider: unknown) => provider is SnapAccountProvider;
//# sourceMappingURL=SnapAccountProvider.d.mts.map