UNPKG

@metamask/multichain-account-service

Version:
1 lines 21.9 kB
{"version":3,"file":"EvmAccountProvider.cjs","sourceRoot":"","sources":["../../src/providers/EvmAccountProvider.ts"],"names":[],"mappings":"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","sourcesContent":["import { publicToAddress } from '@ethereumjs/util';\nimport type { Bip44Account } from '@metamask/account-api';\nimport type { TraceCallback } from '@metamask/controller-utils';\nimport type { HdKeyring } from '@metamask/eth-hd-keyring/v2';\nimport type {\n CreateAccountOptions,\n EntropySourceId,\n KeyringAccount,\n} from '@metamask/keyring-api';\nimport {\n AccountCreationType,\n assertCreateAccountOptionIsSupported,\n EthAccountType,\n EthScope,\n} from '@metamask/keyring-api';\nimport type { KeyringCapabilities, Keyring } from '@metamask/keyring-api/v2';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { AccountId } from '@metamask/keyring-utils';\nimport type { Provider } from '@metamask/network-controller';\nimport { add0x, assert, bytesToHex, isStrictHexString } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { traceFallback } from '../analytics';\nimport { TraceName } from '../analytics/traces';\nimport { projectLogger as log, WARNING_PREFIX } from '../logger';\nimport type { MultichainAccountServiceMessenger } from '../types';\nimport {\n assertAreBip44Accounts,\n assertIsBip44Account,\n BaseBip44AccountProvider,\n} from './BaseBip44AccountProvider';\nimport { withRetry, withTimeout } from './utils';\n\nconst ETH_MAINNET_CHAIN_ID = '0x1';\n\n/**\n * Asserts an internal account exists.\n *\n * @param account - The internal account to check.\n * @throws An error if the internal account does not exist.\n */\nfunction assertInternalAccountExists(\n account: InternalAccount | undefined,\n): asserts account is InternalAccount {\n if (!account) {\n throw new Error('Internal account does not exist');\n }\n}\n\nexport type EvmAccountProviderConfig = {\n discovery: {\n enabled?: boolean;\n maxAttempts: number;\n timeoutMs: number;\n backOffMs: number;\n };\n};\n\nexport const EVM_ACCOUNT_PROVIDER_NAME = 'EVM';\n\nexport const EVM_ACCOUNT_PROVIDER_DEFAULT_CONFIG = {\n discovery: {\n maxAttempts: 3,\n timeoutMs: 500,\n backOffMs: 500,\n },\n};\n\nexport class EvmAccountProvider extends BaseBip44AccountProvider {\n static NAME = EVM_ACCOUNT_PROVIDER_NAME;\n\n readonly #config: EvmAccountProviderConfig;\n\n readonly #trace: TraceCallback;\n\n readonly capabilities: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n deriveIndexRange: true,\n },\n };\n\n constructor(\n messenger: MultichainAccountServiceMessenger,\n config: EvmAccountProviderConfig = EVM_ACCOUNT_PROVIDER_DEFAULT_CONFIG,\n trace?: TraceCallback,\n ) {\n super(messenger);\n this.#config = {\n ...config,\n discovery: {\n ...config.discovery,\n enabled: config.discovery.enabled ?? true,\n },\n };\n this.#trace = trace ?? traceFallback;\n }\n\n isAccountCompatible(account: Bip44Account<InternalAccount>): boolean {\n return (\n account.type === EthAccountType.Eoa &&\n account.metadata.keyring.type === (KeyringTypes.hd as string)\n );\n }\n\n getName(): string {\n return EvmAccountProvider.NAME;\n }\n\n /**\n * Get the EVM provider.\n *\n * @returns The EVM provider.\n */\n getEvmProvider(): Provider {\n const networkClientId = this.messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n ETH_MAINNET_CHAIN_ID,\n );\n const { provider } = this.messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return provider;\n }\n\n /**\n * Create an EVM account.\n *\n * @param opts - The options for the creation of the account.\n * @param opts.entropySource - The entropy source to use for the creation of the account.\n * @param opts.groupIndex - The index of the group to create the account for.\n * @param opts.throwOnGap - Whether to throw an error if the account index is not contiguous.\n * @returns The created or existing keyring account(s) at the requested group\n * index. Returns an empty array if the keyring did not create an account.\n */\n async #createAccount({\n entropySource,\n groupIndex,\n throwOnGap,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n throwOnGap: boolean;\n }): Promise<KeyringAccount[]> {\n return await this.withKeyringV2<Keyring, KeyringAccount[]>(\n { id: entropySource },\n async ({ keyring }) => {\n const existing = await keyring.getAccounts();\n if (groupIndex < existing.length) {\n return [existing[groupIndex]];\n }\n\n // If the throwOnGap flag is set, we throw an error to prevent index gaps.\n if (throwOnGap && groupIndex !== existing.length) {\n throw new Error('Trying to create too many accounts');\n }\n\n return await keyring.createAccounts({\n type: AccountCreationType.Bip44DeriveIndex,\n entropySource,\n groupIndex,\n });\n },\n );\n }\n\n /**\n * Create accounts for the EVM provider.\n *\n * @param options - The options for the creation of the accounts.\n * @returns The accounts for the EVM provider.\n */\n async createAccounts(\n options: CreateAccountOptions,\n ): Promise<Bip44Account<KeyringAccount>[]> {\n assertCreateAccountOptionIsSupported(options, [\n `${AccountCreationType.Bip44DeriveIndex}`,\n `${AccountCreationType.Bip44DeriveIndexRange}`,\n ]);\n\n const { entropySource } = options;\n\n if (options.type === AccountCreationType.Bip44DeriveIndexRange) {\n const { range } = options;\n\n // Use a single withKeyring call for the entire range.\n const accountIds = await this.withKeyringV2<Keyring, AccountId[]>(\n { id: entropySource },\n async ({ keyring }) => {\n const existing = await keyring.getAccounts();\n\n // Validate no gaps: we can only create accounts starting from existing.length.\n if (range.from > existing.length) {\n throw new Error(\n `Bad account creation request, group index range would create gaps (${range.from} (from) > ${existing.length} (next available index))`,\n );\n }\n\n const result: AccountId[] = [];\n\n // Collect existing accounts within the range.\n for (\n let groupIndex = range.from;\n groupIndex <= range.to;\n groupIndex++\n ) {\n if (groupIndex < existing.length) {\n // Account already exists.\n result.push(existing[groupIndex].id);\n }\n }\n\n // Create new accounts one-by-one since HdKeyringV2 only supports\n // bip44:derive-index (not bip44:derive-index-range).\n for (\n let groupIndex = Math.max(range.from, existing.length);\n groupIndex <= range.to;\n groupIndex++\n ) {\n const [created] = await keyring.createAccounts({\n type: AccountCreationType.Bip44DeriveIndex,\n entropySource,\n groupIndex,\n });\n assert(created, 'Account creation failed');\n result.push(created.id);\n }\n\n return result;\n },\n );\n\n const accounts: InternalAccount[] = [];\n for (const account of this.messenger.call(\n 'AccountsController:getAccounts',\n accountIds,\n )) {\n assertInternalAccountExists(account);\n this.accounts.add(account.id);\n accounts.push(account);\n }\n\n assertAreBip44Accounts(accounts);\n return accounts;\n }\n\n // Handle Bip44DeriveIndex (single account creation).\n const { groupIndex } = options;\n\n const [created] = await this.#createAccount({\n entropySource,\n groupIndex,\n throwOnGap: true,\n });\n\n assert(created, 'Account creation failed');\n\n const account = this.messenger.call(\n 'AccountsController:getAccount',\n created.id,\n );\n\n // We MUST have the associated internal account.\n assertInternalAccountExists(account);\n\n const accountsArray = [account];\n assertAreBip44Accounts(accountsArray);\n\n this.accounts.add(account.id);\n return accountsArray;\n }\n\n /**\n * Get the address that would be derived for a given group index without\n * persisting an account in the keyring.\n *\n * Peeks at the next address via the HD `root` so discovery can short-circuit\n * (and skip the vault write + `stateChange` event) when there is no on-chain\n * activity at this index.\n *\n * @param opts - The options for the address derivation.\n * @param opts.entropySource - The entropy source to derive from.\n * @param opts.groupIndex - The group index to derive at.\n * @returns The derived address for the given group index.\n */\n async #getAddressFromGroupIndex({\n entropySource,\n groupIndex,\n }: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): Promise<Hex> {\n // NOTE: To avoid exposing this function at keyring level, we just re-use its internal state\n // and compute the derivation here.\n return await this.withKeyringV2<HdKeyring, Hex>(\n { id: entropySource },\n async ({ keyring }) => {\n // If the account already exist, do not re-derive and just re-use that account.\n const existing = await keyring.getAccounts();\n if (groupIndex < existing.length) {\n return existing[groupIndex].address as Hex;\n }\n\n // If not, then we just \"peek\" the next address to avoid creating the account.\n assert(keyring.root, 'Expected HD keyring.root to be set');\n const hdKey = keyring.root.deriveChild(groupIndex);\n assert(hdKey.publicKey, 'Expected public key to be set');\n\n return add0x(\n bytesToHex(publicToAddress(hdKey.publicKey, true)).toLowerCase(),\n );\n },\n );\n }\n\n /**\n * Get the transaction count for an EVM account.\n * This method uses a retry and timeout mechanism to handle transient failures.\n *\n * @param provider - The provider to use for the transaction count.\n * @param address - The address of the account.\n * @returns The transaction count.\n */\n async #getTransactionCount(\n provider: Provider,\n address: string,\n ): Promise<number> {\n const method = 'eth_getTransactionCount';\n\n const response = await withRetry(\n () =>\n withTimeout(\n () =>\n provider.request({\n method,\n params: [address, 'latest'],\n }),\n this.#config.discovery.timeoutMs,\n ),\n {\n maxAttempts: this.#config.discovery.maxAttempts,\n backOffMs: this.#config.discovery.backOffMs,\n },\n );\n\n // Make sure we got the right response format, if not, we fallback to \"0x0\", to avoid having to deal with `NaN`.\n if (!isStrictHexString(response)) {\n const message = `Received invalid hex response from \"${method}\" request: ${JSON.stringify(response)}`;\n\n log(`${WARNING_PREFIX} ${message}`);\n console.warn(message);\n\n return 0;\n }\n\n return parseInt(response, 16);\n }\n\n /**\n * Discover and create accounts for the EVM provider.\n *\n * @param opts - The options for the discovery and creation of accounts.\n * @param opts.entropySource - The entropy source to use for the discovery and creation of accounts.\n * @param opts.groupIndex - The index of the group to create the accounts for.\n * @returns The accounts for the EVM provider.\n */\n async discoverAccounts(opts: {\n entropySource: EntropySourceId;\n groupIndex: number;\n }): Promise<Bip44Account<KeyringAccount>[]> {\n return this.#trace(\n {\n name: TraceName.EvmDiscoverAccounts,\n data: {\n provider: this.getName(),\n },\n },\n async () => {\n if (!this.#config.discovery.enabled) {\n return [];\n }\n\n const provider = this.getEvmProvider();\n const { entropySource, groupIndex } = opts;\n\n const addressFromGroupIndex = await this.#getAddressFromGroupIndex({\n entropySource,\n groupIndex,\n });\n\n const count = await this.#getTransactionCount(\n provider,\n addressFromGroupIndex,\n );\n if (count === 0) {\n return [];\n }\n\n // We have some activity on this address, we try to create the account.\n const [created] = await this.#createAccount({\n entropySource,\n groupIndex,\n throwOnGap: false,\n });\n\n assert(created, 'Account creation failed');\n assert(\n addressFromGroupIndex === created.address,\n 'Created account does not match address from group index.',\n );\n\n const account = this.messenger.call(\n 'AccountsController:getAccount',\n created.id,\n );\n assertInternalAccountExists(account);\n assertIsBip44Account(account);\n this.accounts.add(account.id);\n return [account];\n },\n );\n }\n\n async resyncAccounts(): Promise<void> {\n // No-op for the EVM account provider, since keyring accounts are already on\n // the MetaMask side.\n }\n\n /**\n * Delete an EVM account by id.\n *\n * Resolves the account's entropy source from the tracked account, then\n * forwards to the v2 HD keyring's `deleteAccount(id)`. When this is the\n * last account on a non-primary HD keyring, the keyring controller will\n * automatically prune the empty keyring (see\n * `KeyringController.#cleanUpEmptiedKeyringsAfter`).\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 const entropySource = account.options.entropy.id;\n\n await this.withKeyringV2<Keyring>(\n { id: entropySource },\n async ({ keyring }) => {\n await keyring.deleteAccount(id);\n },\n );\n\n this.accounts.delete(id);\n }\n}\n"]}