UNPKG

@metamask/multichain-account-service

Version:
666 lines 38.4 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _MultichainAccountWallet_instances, _MultichainAccountWallet_lock, _MultichainAccountWallet_id, _MultichainAccountWallet_providers, _MultichainAccountWallet_entropySource, _MultichainAccountWallet_accountGroups, _MultichainAccountWallet_messenger, _MultichainAccountWallet_trace, _MultichainAccountWallet_log, _MultichainAccountWallet_initialized, _MultichainAccountWallet_status, _MultichainAccountWallet_withLock, _MultichainAccountWallet_getProviders, _MultichainAccountWallet_createAccountsRangeForProvider, _MultichainAccountWallet_createOrUpdateMultichainAccountGroup, _MultichainAccountWallet_buildGroupState, _MultichainAccountWallet_getUnalignedSubRangesForProvider, _MultichainAccountWallet_createMultichainAccountGroupsRange, _MultichainAccountWallet_alignAccountsForRange, _MultichainAccountWallet_createMultichainAccountGroups; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultichainAccountWallet = void 0; const account_api_1 = require("@metamask/account-api"); const keyring_api_1 = require("@metamask/keyring-api"); const utils_1 = require("@metamask/utils"); const async_mutex_1 = require("async-mutex"); const analytics_1 = require("./analytics/index.cjs"); const errors_1 = require("./errors.cjs"); const logger_1 = require("./logger.cjs"); const MultichainAccountGroup_1 = require("./MultichainAccountGroup.cjs"); const EvmAccountProvider_1 = require("./providers/EvmAccountProvider.cjs"); const utils_2 = require("./utils.cjs"); /** * A multichain account wallet that holds multiple multichain accounts (one multichain account per * group index). */ class MultichainAccountWallet { constructor({ providers, entropySource, messenger, trace, }) { _MultichainAccountWallet_instances.add(this); _MultichainAccountWallet_lock.set(this, new async_mutex_1.Mutex()); _MultichainAccountWallet_id.set(this, void 0); _MultichainAccountWallet_providers.set(this, void 0); _MultichainAccountWallet_entropySource.set(this, void 0); _MultichainAccountWallet_accountGroups.set(this, void 0); _MultichainAccountWallet_messenger.set(this, void 0); _MultichainAccountWallet_trace.set(this, void 0); _MultichainAccountWallet_log.set(this, void 0); _MultichainAccountWallet_initialized.set(this, false); _MultichainAccountWallet_status.set(this, void 0); __classPrivateFieldSet(this, _MultichainAccountWallet_id, (0, account_api_1.toMultichainAccountWalletId)(entropySource), "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_providers, providers, "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_entropySource, entropySource, "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_messenger, messenger, "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_accountGroups, new Map(), "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_trace, trace ?? analytics_1.traceFallback, "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_log, (0, logger_1.createModuleLogger)(logger_1.projectLogger, `[${__classPrivateFieldGet(this, _MultichainAccountWallet_id, "f")}]`), "f"); // Initial synchronization (don't emit events during initialization). __classPrivateFieldSet(this, _MultichainAccountWallet_status, 'uninitialized', "f"); } /** * Initialize the wallet and construct the internal representation of multichain account groups. * * @param walletState - The wallet state. */ init(walletState) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Initializing wallet state...'); for (const [groupIndexString, groupState] of Object.entries(walletState)) { // Have to convert to number because the state keys become strings when we construct the state object in the service const groupIndex = Number(groupIndexString); const group = new MultichainAccountGroup_1.MultichainAccountGroup({ groupIndex, wallet: this, providers: __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"), messenger: __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f"), }); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Creating new group for index ${groupIndex}...`); group.init(groupState); __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").set(groupIndex, group); } if (!__classPrivateFieldGet(this, _MultichainAccountWallet_initialized, "f")) { __classPrivateFieldSet(this, _MultichainAccountWallet_initialized, true, "f"); __classPrivateFieldSet(this, _MultichainAccountWallet_status, 'ready', "f"); } __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Finished initializing wallet state...'); } /** * Gets the multichain account wallet ID. * * @returns The multichain account wallet ID. */ get id() { return __classPrivateFieldGet(this, _MultichainAccountWallet_id, "f"); } /** * Gets the multichain account wallet type, which is always {@link AccountWalletType.Entropy}. * * @returns The multichain account wallet type. */ get type() { return account_api_1.AccountWalletType.Entropy; } /** * Gets the multichain account wallet entropy source. * * @returns The multichain account wallet entropy source. */ get entropySource() { return __classPrivateFieldGet(this, _MultichainAccountWallet_entropySource, "f"); } /** * Gets the multichain account wallet current status. * * @returns The multichain account wallet current status. */ get status() { return __classPrivateFieldGet(this, _MultichainAccountWallet_status, "f"); } /** * Gets multichain account for a given ID. * The default group ID will default to the multichain account with index 0. * * @param id - Account group ID. * @returns Account group. */ getAccountGroup(id) { // We consider the "default case" to be mapped to index 0. if (id === (0, account_api_1.toDefaultAccountGroupId)(this.id)) { return __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").get(0); } // If it is not a valid ID, we cannot extract the group index // from it, so we fail fast. if (!(0, account_api_1.isMultichainAccountGroupId)(id)) { return undefined; } const groupIndex = (0, account_api_1.getGroupIndexFromMultichainAccountGroupId)(id); return __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").get(groupIndex); } /** * Gets all multichain accounts. Similar to {@link MultichainAccountWallet.getMultichainAccountGroups}. * * @returns The multichain accounts. */ getAccountGroups() { return this.getMultichainAccountGroups(); } /** * Gets multichain account group for a given index. * * @param groupIndex - Multichain account index. * @returns The multichain account associated with the given index. */ getMultichainAccountGroup(groupIndex) { return __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").get(groupIndex); } /** * Gets all multichain account groups. * * @returns The multichain accounts. */ getMultichainAccountGroups() { return Array.from(__classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").values()); // TODO: Prevent copy here. } /** * Gets next group index for this wallet. * * @returns The next group index of this wallet. */ getNextGroupIndex() { // We do not check for gaps. return (Math.max(-1, // So it will default to 0 if no groups. ...__classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").keys()) + 1); } /** * Creates a multichain account group for a given group index. * * NOTE: This operation WILL lock the wallet's mutex. * * @param groupIndex - The group index to use. * @param options - Options to configure the account creation. * @param options.waitForAllProvidersToFinishCreatingAccounts - Whether to wait for all * account providers to finish creating their accounts before returning. If `false`, only * the EVM provider is used and non-EVM account creation is deferred via * {@link MultichainAccountWallet.alignAccountsOf}. Defaults to `false`. * @throws If groupIndex is greater than the next available group index. * @throws If any account provider fails to create accounts. * @returns The multichain account group for this group index. */ async createMultichainAccountGroup(groupIndex, options = {}) { // Use this to avoid having it as `boolean | undefined`. const waitForAllProvidersToFinishCreatingAccounts = options.waitForAllProvidersToFinishCreatingAccounts ?? false; return await __classPrivateFieldGet(this, _MultichainAccountWallet_trace, "f").call(this, { name: analytics_1.TraceName.WalletCreateMultichainAccountGroup, data: { groupIndex, waitForAllProvidersToFinishCreatingAccounts, }, }, async () => { (0, utils_2.assertGroupIndexIsValid)(groupIndex, this.getNextGroupIndex()); // If the group already exists, return it. const existingGroup = this.getMultichainAccountGroup(groupIndex); if (existingGroup) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Trying to re-create existing group: [${existingGroup.id}] (idempotent)`); return existingGroup; } // Create a single group with a range of 1 (so we can reuse the batch creation logic) for the // given group index. const groups = await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createMultichainAccountGroups).call(this, { from: groupIndex, to: groupIndex }, options); const group = groups[0]; (0, utils_1.assert)(group, `Expected group at index ${groupIndex} to exist`); return group; }); } /** * Creates multiple multichain account groups up to maxGroupIndex. * * NOTE: This operation WILL lock the wallet's mutex. * * @param range - The range of group indices to create. * @param range.from - Starting group index to create (inclusive) (defaults to 0). * @param range.to - Maximum group index to create (inclusive). * @param options - Options to configure the account creation. * @param options.waitForAllProvidersToFinishCreatingAccounts - Whether to wait for all * account providers to finish creating their accounts before returning. If `false`, only * the EVM provider is used and non-EVM account creation is deferred via * {@link MultichainAccountWallet.alignAccounts}. Defaults to false. * @throws If range is invalid (e.g. from is greater than to, from or to is negative, etc.). * @returns Array of created multichain account groups. */ async createMultichainAccountGroups({ from = 0, to }, options = {}) { // Use this to avoid having it as `boolean | undefined`. const waitForAllProvidersToFinishCreatingAccounts = options.waitForAllProvidersToFinishCreatingAccounts ?? false; return await __classPrivateFieldGet(this, _MultichainAccountWallet_trace, "f").call(this, { name: analytics_1.TraceName.WalletCreateMultichainAccountGroups, data: { from, to, waitForAllProvidersToFinishCreatingAccounts, }, }, async () => await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createMultichainAccountGroups).call(this, { from, to }, options)); } /** * Creates the next multichain account group. * * @throws If any of the account providers fails to create their accounts. * @returns The multichain account group for the next group index available. */ async createNextMultichainAccountGroup() { return this.createMultichainAccountGroup(this.getNextGroupIndex(), { waitForAllProvidersToFinishCreatingAccounts: true, }); } /** * Align all accounts from each existing multichain account groups. * * NOTE: This operation WILL lock the wallet's mutex. */ async alignAccounts() { if (this.isAligned()) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Already aligned, skipping...'); return; } const nextGroupIndex = this.getNextGroupIndex(); if (nextGroupIndex > 0) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Aligning accounts...'); const from = 0; const to = nextGroupIndex - 1; await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_withLock).call(this, 'in-progress:alignment', async () => await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_alignAccountsForRange).call(this, { from, to }, __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"))); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Aligned!'); } } /** * Check whether every group in this wallet is aligned. * * A wallet is aligned when every multichain account group reports that all * of its registered providers have contributed at least one account. * Returns `true` if the wallet has no groups. * * @returns `true` when all groups are aligned. */ isAligned() { return this.getMultichainAccountGroups().every((group) => group.isAligned()); } /** * Align a specific multichain account group. * * NOTE: This operation WILL lock the wallet's mutex. * * @param groupIndex - The group index to align. */ async alignAccountsOf(groupIndex) { const group = this.getMultichainAccountGroup(groupIndex); if (group) { if (group.isAligned()) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Group "${group.id}" is already aligned, skipping...`); return; } __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Aligning accounts for group "${group.id}"...`); await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_withLock).call(this, 'in-progress:alignment', async () => await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_alignAccountsForRange).call(this, { from: groupIndex, to: groupIndex }, __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"), { trace: { data: { groupIndex } } })); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Aligned accounts for group "${group.id}"!`); } } /** * Discover and create accounts for all providers. * * NOTE: This operation WILL lock the wallet's mutex. * * @returns The discovered accounts for each provider. */ async discoverAccounts() { return __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_withLock).call(this, 'in-progress:discovery', async () => { // Start with the next available group index (so we can resume the discovery // from there). let maxGroupIndex = this.getNextGroupIndex(); const discoveredGroupsState = {}; const addDiscoveryResultToState = (result, providerName, groupIndex) => { const accountIds = result.map((account) => account.id); discoveredGroupsState[groupIndex] ?? (discoveredGroupsState[groupIndex] = {}); discoveredGroupsState[groupIndex][providerName] = accountIds; }; // One serialized loop per provider; all run concurrently const runProviderDiscovery = async (context) => { const providerName = context.provider.getName(); const message = (stepName, groupIndex) => `[${providerName}] Discovery ${stepName} for group index: ${groupIndex}`; while (!context.stopped) { // Fast‑forward to current high‑water mark const targetGroupIndex = Math.max(context.groupIndex, maxGroupIndex); (0, logger_1.projectLogger)(message('started', targetGroupIndex)); let accounts = []; try { accounts = await context.provider.discoverAccounts({ entropySource: __classPrivateFieldGet(this, _MultichainAccountWallet_entropySource, "f"), groupIndex: targetGroupIndex, }); } catch (error) { context.stopped = true; (0, logger_1.projectLogger)(message(`failed (with: "${(0, utils_2.toErrorMessage)(error)}")`, targetGroupIndex)); (0, errors_1.reportError)(__classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f"), `Unable to discover accounts with provider "${providerName}"`, error, { provider: providerName, groupIndex: targetGroupIndex, }); break; } if (!accounts.length) { (0, logger_1.projectLogger)(message('stopped (no accounts got discovered)', targetGroupIndex)); context.stopped = true; break; } (0, logger_1.projectLogger)(message('**succeeded**', targetGroupIndex)); context.accounts = context.accounts.concat(accounts); addDiscoveryResultToState(accounts, providerName, targetGroupIndex); const nextGroupIndex = targetGroupIndex + 1; context.groupIndex = nextGroupIndex; if (nextGroupIndex > maxGroupIndex) { maxGroupIndex = nextGroupIndex; } } }; const providerContexts = __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f").map((provider) => ({ provider, stopped: false, groupIndex: maxGroupIndex, accounts: [], })); // Start discovery for each providers. await Promise.all(providerContexts.map(runProviderDiscovery)); // Create discovered groups for (const [groupIndexString, groupState] of Object.entries(discoveredGroupsState)) { const groupIndex = Number(groupIndexString); const group = new MultichainAccountGroup_1.MultichainAccountGroup({ wallet: this, providers: __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"), groupIndex, messenger: __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f"), }); group.init(groupState); __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").set(groupIndex, group); } // Align missing accounts from group. This is required to create missing account from non-discovered // indexes for some providers. const nextGroupIndex = this.getNextGroupIndex(); if (nextGroupIndex > 0) { await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_alignAccountsForRange).call(this, { from: 0, to: nextGroupIndex - 1 }, __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"), { trace: { data: { discovery: true, // Tag to identify discovery-alignment traces in analytics. }, }, }); } return providerContexts.flatMap((context) => context.accounts); }); } } exports.MultichainAccountWallet = MultichainAccountWallet; _MultichainAccountWallet_lock = new WeakMap(), _MultichainAccountWallet_id = new WeakMap(), _MultichainAccountWallet_providers = new WeakMap(), _MultichainAccountWallet_entropySource = new WeakMap(), _MultichainAccountWallet_accountGroups = new WeakMap(), _MultichainAccountWallet_messenger = new WeakMap(), _MultichainAccountWallet_trace = new WeakMap(), _MultichainAccountWallet_log = new WeakMap(), _MultichainAccountWallet_initialized = new WeakMap(), _MultichainAccountWallet_status = new WeakMap(), _MultichainAccountWallet_instances = new WeakSet(), _MultichainAccountWallet_withLock = /** * Set the wallet status and run the associated operation callback. * * @param status - Wallet status associated with this operation. * @param operation - Operation to run. * @returns The operation's result. * @throws {Error} If the wallet is already running a mutable operation. */ async function _MultichainAccountWallet_withLock(status, operation) { const release = await __classPrivateFieldGet(this, _MultichainAccountWallet_lock, "f").acquire(); try { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Locking wallet with status "${status}"...`); __classPrivateFieldSet(this, _MultichainAccountWallet_status, status, "f"); __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f").publish('MultichainAccountService:walletStatusChange', this.id, __classPrivateFieldGet(this, _MultichainAccountWallet_status, "f")); return await operation(); } finally { __classPrivateFieldSet(this, _MultichainAccountWallet_status, 'ready', "f"); __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f").publish('MultichainAccountService:walletStatusChange', this.id, __classPrivateFieldGet(this, _MultichainAccountWallet_status, "f")); release(); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Releasing wallet lock (was "${status}")`); } }, _MultichainAccountWallet_getProviders = function _MultichainAccountWallet_getProviders() { const [evmProvider, ...otherProviders] = __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"); (0, utils_1.assert)(evmProvider instanceof EvmAccountProvider_1.EvmAccountProvider, 'EVM account provider must be first'); return [evmProvider, ...otherProviders]; }, _MultichainAccountWallet_createAccountsRangeForProvider = /** * Create accounts for a given provider and group index range. * * @param provider - The provider to create accounts for. * @param from - The starting group index (inclusive). * @param to - The ending group index (inclusive). * @returns The created accounts. */ async function _MultichainAccountWallet_createAccountsRangeForProvider(provider, from, to) { const isBatching = to > from; try { return await provider.createAccounts({ type: keyring_api_1.AccountCreationType.Bip44DeriveIndexRange, entropySource: __classPrivateFieldGet(this, _MultichainAccountWallet_entropySource, "f"), range: { from, to, }, }); } catch (error) { (0, errors_1.reportError)(__classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f"), `Unable to create ${isBatching ? 'some accounts (batch)' : 'some accounts'} with provider "${provider.getName()}"`, error, { range: { from, to }, provider: provider.getName(), isBatching, }); throw error; } }, _MultichainAccountWallet_createOrUpdateMultichainAccountGroup = function _MultichainAccountWallet_createOrUpdateMultichainAccountGroup(groupIndex, groupState) { let group = __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").get(groupIndex); if (group) { // NOTE: This will publish an update event automatically. group.update(groupState); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Group updated: [${group.id}]`); } else { group = new MultichainAccountGroup_1.MultichainAccountGroup({ wallet: this, providers: __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f"), groupIndex, messenger: __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f"), }); group.init(groupState); __classPrivateFieldGet(this, _MultichainAccountWallet_accountGroups, "f").set(groupIndex, group); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Group created: [${group.id}]`); if (__classPrivateFieldGet(this, _MultichainAccountWallet_initialized, "f")) { __classPrivateFieldGet(this, _MultichainAccountWallet_messenger, "f").publish('MultichainAccountService:multichainAccountGroupCreated', group); } } return group; }, _MultichainAccountWallet_buildGroupState = /** * Build group state by calling all providers in parallel. * * This is a non-locking shared core used by both creation and alignment paths. * Each provider is asked which contiguous sub-ranges of group indices it needs * accounts created for via `getSubRanges`, so callers can skip indices that are * already satisfied (e.g. during alignment). * * @param providers - The providers to create accounts for. * @param getSubRanges - Resolver returning the sub-ranges to create for a * given provider. Returning an empty array means the provider is skipped. * @returns The collected group state and any provider failure messages. */ async function _MultichainAccountWallet_buildGroupState(providers, getSubRanges) { const groupStateByGroupIndex = new Map(); const results = await Promise.allSettled(providers.map(async (provider) => { const providerName = provider.getName(); const subRanges = getSubRanges(provider); for (const { from, to } of subRanges) { const accounts = await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createAccountsRangeForProvider).call(this, provider, from, to); accounts.forEach((account) => { const { groupIndex } = account.options.entropy; let groupState = groupStateByGroupIndex.get(groupIndex); if (!groupState) { groupState = {}; groupStateByGroupIndex.set(groupIndex, groupState); } if (!groupState[providerName]) { groupState[providerName] = []; } groupState[providerName].push(account.id); }); } })); const failures = providers.reduce((messages, provider, index) => { const result = results[index]; if (result?.status === 'rejected') { messages.push(`[${provider.getName()}] ${(0, utils_2.toErrorMessage)(result.reason)}`); } return messages; }, []); return { groupStateByGroupIndex, failures }; }, _MultichainAccountWallet_getUnalignedSubRangesForProvider = function _MultichainAccountWallet_getUnalignedSubRangesForProvider(provider, from, to) { const subRanges = []; let runStart; for (let groupIndex = from; groupIndex <= to; groupIndex++) { const group = this.getMultichainAccountGroup(groupIndex); const aligned = group ? group.isProviderAligned(provider) : false; if (!aligned) { runStart ?? (runStart = groupIndex); } else if (runStart !== undefined) { subRanges.push({ from: runStart, to: groupIndex - 1 }); runStart = undefined; } } if (runStart !== undefined) { subRanges.push({ from: runStart, to }); } return subRanges; }, _MultichainAccountWallet_createMultichainAccountGroupsRange = /** * Internal method to create a range of multichain account groups. * * This method acquires the wallet lock internally and creates accounts for all * given providers synchronously. Callers decide which providers to pass. * * @param range - The range of group indices to create. * @param range.from - Starting group index to create (inclusive). * @param range.to - Maximum group index to create (inclusive). * @param providers - The providers to create accounts for. * @returns Array of created multichain account groups. */ async function _MultichainAccountWallet_createMultichainAccountGroupsRange({ from: rangeFrom, to }, providers) { return await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_withLock).call(this, 'in-progress:create-accounts', async () => { const groups = []; // Get existing groups (fromGroupIndex to nextGroupIndex - 1). let from = rangeFrom; for (; from <= to; from++) { const group = this.getMultichainAccountGroup(from); if (group) { groups.push(group); } else { break; // Assuming we have no gap, if the group does not exist, we can stop and create the remaining ones. } } // Create new groups now. if (from <= to) { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Creating groups from index ${from} to ${to}...`); const { groupStateByGroupIndex, failures } = await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_buildGroupState).call(this, providers, () => [{ from, to }]); // Check for provider failures — always treated as hard errors. if (failures.length) { throw new Error(failures.reduce((message, failure) => `${message}\n- ${failure}`, 'Unable to create some accounts. Providers threw the following errors:')); } // Create or update groups from the collected state. for (let groupIndex = from; groupIndex <= to; groupIndex++) { const groupState = groupStateByGroupIndex.get(groupIndex); if (groupState) { const group = __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createOrUpdateMultichainAccountGroup).call(this, groupIndex, groupState); groups.push(group); } else { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `${logger_1.WARNING_PREFIX} Failed to create new group for group index: ${groupIndex} because no accounts were created for it`); } } } return groups; }); }, _MultichainAccountWallet_alignAccountsForRange = /** * Align accounts for a range of group indices (non-locking). * * Calls all providers in parallel via the batch API. Provider failures are * logged as warnings (best-effort); no error is thrown. * * @param range - The range of group indices to align. * @param range.from - Starting group index (inclusive). * @param range.to - Ending group index (inclusive). * @param providers - The providers to align accounts for. * @param options - Options. * @param options.trace - Trace options. * @param options.trace.data - Optional trace data. */ async function _MultichainAccountWallet_alignAccountsForRange({ from, to }, providers, options = {}) { await __classPrivateFieldGet(this, _MultichainAccountWallet_trace, "f").call(this, { name: analytics_1.TraceName.WalletAlignment, data: { from, to, ...(0, analytics_1.toProviderDataTraces)(providers), ...options.trace?.data, }, }, async () => { const { groupStateByGroupIndex, failures } = await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_buildGroupState).call(this, providers, (provider) => __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_getUnalignedSubRangesForProvider).call(this, provider, from, to)); if (failures.length) { const error = failures.reduce((message, failure) => `${message}\n- ${failure}`, 'Unable to align some accounts. Providers threw the following errors:'); console.warn(error); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `${logger_1.WARNING_PREFIX} ${error}`); } for (let groupIndex = from; groupIndex <= to; groupIndex++) { const groupState = groupStateByGroupIndex.get(groupIndex); if (groupState) { __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createOrUpdateMultichainAccountGroup).call(this, groupIndex, groupState); } } }); }, _MultichainAccountWallet_createMultichainAccountGroups = /** * Creates multiple multichain account groups up to maxGroupIndex. * * NOTE: This operation WILL lock the wallet's mutex. * * @param range - The range of group indices to create. * @param range.from - Starting group index to create (inclusive). * @param range.to - Maximum group index to create (inclusive). * @param options - Options to configure the account creation. * @param options.waitForAllProvidersToFinishCreatingAccounts - Whether to wait for all * account providers to finish creating their accounts before returning. If `false`, only * the EVM provider is used and non-EVM account creation is deferred via * {@link MultichainAccountWallet.alignAccounts}. Defaults to false. * @throws If range is invalid (e.g. from is greater than to, from or to is negative, etc.). * @returns Array of created multichain account groups. */ async function _MultichainAccountWallet_createMultichainAccountGroups({ from, to }, options) { (0, utils_2.assertGroupIndexRangeIsValid)({ from, to }); (0, utils_2.assertGroupIndexIsValid)(from, this.getNextGroupIndex()); const waitForAllProvidersToFinishCreatingAccounts = options.waitForAllProvidersToFinishCreatingAccounts ?? false; const [evmProvider, ...otherProviders] = __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_getProviders).call(this); const providers = waitForAllProvidersToFinishCreatingAccounts ? __classPrivateFieldGet(this, _MultichainAccountWallet_providers, "f") : [evmProvider]; const groups = await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_createMultichainAccountGroupsRange).call(this, { from, to }, providers); // We need to run a post-alignment since non-EVM accounts have not // been created yet. if (!waitForAllProvidersToFinishCreatingAccounts) { const alignOtherAccounts = async () => { __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `Aligning accounts... (post)`); await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_withLock).call(this, 'in-progress:alignment', async () => { await __classPrivateFieldGet(this, _MultichainAccountWallet_instances, "m", _MultichainAccountWallet_alignAccountsForRange).call(this, { from, to }, otherProviders, { trace: { data: { post: true, // Tag to identify post-alignment traces in analytics. }, }, }); }); __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, 'Aligned accounts! (post)'); }; // eslint-disable-next-line no-void void alignOtherAccounts().catch((error) => { const errorMessage = `Unable to align non-EVM accounts from group index ${from} to ${to}`; __classPrivateFieldGet(this, _MultichainAccountWallet_log, "f").call(this, `${logger_1.ERROR_PREFIX} ${errorMessage}: ${(0, utils_2.toErrorMessage)(error)} (post)`); console.error(errorMessage, error); }); } return groups; }; //# sourceMappingURL=MultichainAccountWallet.cjs.map