@metamask-previews/account-api
Version:
MetaMask Multichain Account API
235 lines • 13.7 kB
JavaScript
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
// This rule seems to be triggering a false positive. Possibly eslint is not
// inferring the `InternalAccount` type correctly which causes issue with the
// union `| undefined`.
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 _MultichainAccountAdapter_id, _MultichainAccountAdapter_wallet, _MultichainAccountAdapter_index, _MultichainAccountAdapter_providers, _MultichainAccountAdapter_providersByAccountId, _MultichainAccountAdapter_accounts, _MultichainAccountWalletAdapter_instances, _MultichainAccountWalletAdapter_id, _MultichainAccountWalletAdapter_providers, _MultichainAccountWalletAdapter_entropySource, _MultichainAccountWalletAdapter_accounts, _MultichainAccountWalletAdapter_createMultichainAccount;
import { isScopeEqualToAny } from "@metamask/keyring-utils";
import { getGroupIndexFromAccountGroupId, toDefaultAccountGroupId, toMultichainAccountId, toMultichainAccountWalletId } from "../index.mjs";
export class MultichainAccountAdapter {
constructor({ groupIndex, wallet, providers, }) {
_MultichainAccountAdapter_id.set(this, void 0);
_MultichainAccountAdapter_wallet.set(this, void 0);
_MultichainAccountAdapter_index.set(this, void 0);
_MultichainAccountAdapter_providers.set(this, void 0);
_MultichainAccountAdapter_providersByAccountId.set(this, void 0);
_MultichainAccountAdapter_accounts.set(this, void 0);
__classPrivateFieldSet(this, _MultichainAccountAdapter_id, toMultichainAccountId(wallet.id, groupIndex), "f");
__classPrivateFieldSet(this, _MultichainAccountAdapter_index, groupIndex, "f");
__classPrivateFieldSet(this, _MultichainAccountAdapter_wallet, wallet, "f");
__classPrivateFieldSet(this, _MultichainAccountAdapter_providers, providers, "f");
__classPrivateFieldSet(this, _MultichainAccountAdapter_accounts, new Map(), "f");
__classPrivateFieldSet(this, _MultichainAccountAdapter_providersByAccountId, new Map(), "f");
for (const provider of __classPrivateFieldGet(this, _MultichainAccountAdapter_providers, "f")) {
const accounts = provider.getAccounts({
entropySource: __classPrivateFieldGet(this, _MultichainAccountAdapter_wallet, "f").entropySource,
groupIndex: __classPrivateFieldGet(this, _MultichainAccountAdapter_index, "f"),
});
__classPrivateFieldGet(this, _MultichainAccountAdapter_accounts, "f").set(provider, accounts);
for (const id of accounts) {
__classPrivateFieldGet(this, _MultichainAccountAdapter_providersByAccountId, "f").set(id, provider);
}
}
}
get id() {
return __classPrivateFieldGet(this, _MultichainAccountAdapter_id, "f");
}
get wallet() {
return __classPrivateFieldGet(this, _MultichainAccountAdapter_wallet, "f");
}
get index() {
return __classPrivateFieldGet(this, _MultichainAccountAdapter_index, "f");
}
hasAccounts() {
// Use this map, cause if there's no accounts, then this map will also
// be empty.
return __classPrivateFieldGet(this, _MultichainAccountAdapter_providersByAccountId, "f").size > 0;
}
getAccounts() {
let allAccounts = [];
for (const [provider, accounts] of __classPrivateFieldGet(this, _MultichainAccountAdapter_accounts, "f").entries()) {
allAccounts = allAccounts.concat(accounts.map((id) => provider.getAccount(id)));
}
return allAccounts;
}
getAccount(id) {
const provider = __classPrivateFieldGet(this, _MultichainAccountAdapter_providersByAccountId, "f").get(id);
return provider?.getAccount(id);
}
get(selector) {
const accounts = this.select(selector);
if (accounts.length > 1) {
throw new Error(`Too many account candidates, expected 1, got: ${accounts.length}`);
}
if (accounts.length === 0) {
return undefined;
}
return accounts[0]; // This is safe, see checks above.
}
select(selector) {
return this.getAccounts().filter((account) => {
let selected = true;
if (selector.id) {
selected && (selected = account.id === selector.id);
}
if (selector.address) {
selected && (selected = account.address === selector.address);
}
if (selector.type) {
selected && (selected = account.type === selector.type);
}
if (selector.methods !== undefined) {
selected && (selected = selector.methods.some((method) => account.methods.includes(method)));
}
if (selector.scopes !== undefined) {
selected && (selected = selector.scopes.some((scope) => {
return (
// This will cover specific EVM EOA scopes as well.
isScopeEqualToAny(scope, account.scopes));
}));
}
return selected;
});
}
}
_MultichainAccountAdapter_id = new WeakMap(), _MultichainAccountAdapter_wallet = new WeakMap(), _MultichainAccountAdapter_index = new WeakMap(), _MultichainAccountAdapter_providers = new WeakMap(), _MultichainAccountAdapter_providersByAccountId = new WeakMap(), _MultichainAccountAdapter_accounts = new WeakMap();
export class MultichainAccountWalletAdapter {
constructor({ providers, entropySource, }) {
_MultichainAccountWalletAdapter_instances.add(this);
_MultichainAccountWalletAdapter_id.set(this, void 0);
_MultichainAccountWalletAdapter_providers.set(this, void 0);
_MultichainAccountWalletAdapter_entropySource.set(this, void 0);
_MultichainAccountWalletAdapter_accounts.set(this, void 0);
__classPrivateFieldSet(this, _MultichainAccountWalletAdapter_id, toMultichainAccountWalletId(entropySource), "f");
__classPrivateFieldSet(this, _MultichainAccountWalletAdapter_providers, providers, "f");
__classPrivateFieldSet(this, _MultichainAccountWalletAdapter_entropySource, entropySource, "f");
__classPrivateFieldSet(this, _MultichainAccountWalletAdapter_accounts, new Map(), "f");
let groupIndex = 0;
let hasAccounts = false;
do {
const multichainAccount = new MultichainAccountAdapter({
groupIndex,
wallet: this,
providers: __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_providers, "f"),
});
// We only add multichain account that has underlying accounts.
hasAccounts = multichainAccount.hasAccounts();
if (hasAccounts) {
__classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").set(groupIndex, multichainAccount);
}
groupIndex += 1;
} while (hasAccounts);
}
get id() {
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_id, "f");
}
get entropySource() {
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_entropySource, "f");
}
getAccountGroup(groupId) {
// We consider the "default case" to be mapped to index 0.
if (groupId === toDefaultAccountGroupId(this.id)) {
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").get(0);
}
const groupIndex = getGroupIndexFromAccountGroupId(groupId);
if (groupIndex === undefined) {
return undefined;
}
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").get(groupIndex);
}
getAccountGroups() {
return this.getMultichainAccounts();
}
getMultichainAccount(groupIndex) {
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").get(groupIndex);
}
getMultichainAccounts() {
return Array.from(__classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").values()); // TODO: Prevent copy here.
}
getNextGroupIndex() {
// Assuming we cannot have indexes gaps.
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").size; // No +1 here, group indexes starts at 0.
}
async createMultichainAccount(groupIndex) {
const nextGroupIndex = this.getNextGroupIndex();
if (groupIndex > nextGroupIndex) {
throw new Error(`You cannot use an group index that is higher than the next available one: expect <${nextGroupIndex}, got ${groupIndex}`);
}
for (const provider of __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_providers, "f")) {
await provider.createAccounts({
entropySource: __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_entropySource, "f"),
groupIndex,
});
}
// Re-create and "refresh" the multichain account (we assume all account creations are
// idempotent, so we should get the same accounts and potentially some new accounts (if
// some account providers decide to return more of them this time).
return __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_instances, "m", _MultichainAccountWalletAdapter_createMultichainAccount).call(this, groupIndex);
}
async createNextMultichainAccount() {
return this.createMultichainAccount(this.getNextGroupIndex());
}
async discoverAndCreateMultichainAccounts() {
const multichainAccounts = [];
let discovered;
let groupIndex = 0;
do {
// Keep track if any accounts got discovered for that new index.
discovered = false;
const missingProviders = [];
for (const provider of __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_providers, "f")) {
const discoveredAccounts = await provider.discoverAndCreateAccounts({
entropySource: __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_entropySource, "f"),
groupIndex,
});
if (discoveredAccounts.length) {
// This provider has discovered and created accounts, meaning there might
// be something to discover on the next index.
discovered = true;
}
else {
// This provider did not discover or create any accounts. We mark it as
// "missing", so we can create accounts on this index if other providers
// did discover something.
missingProviders.push(provider);
}
}
if (discovered) {
// We only create missing accounts if one of the provider has discovered
// and created accounts.
for (const provider of missingProviders) {
await provider.createAccounts({
entropySource: __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_entropySource, "f"),
groupIndex,
});
}
// We've got all the accounts now, we can create our multichain account.
multichainAccounts.push(__classPrivateFieldGet(this, _MultichainAccountWalletAdapter_instances, "m", _MultichainAccountWalletAdapter_createMultichainAccount).call(this, groupIndex));
// We have accounts, we need to check the next index.
groupIndex += 1;
}
} while (discovered);
return multichainAccounts;
}
}
_MultichainAccountWalletAdapter_id = new WeakMap(), _MultichainAccountWalletAdapter_providers = new WeakMap(), _MultichainAccountWalletAdapter_entropySource = new WeakMap(), _MultichainAccountWalletAdapter_accounts = new WeakMap(), _MultichainAccountWalletAdapter_instances = new WeakSet(), _MultichainAccountWalletAdapter_createMultichainAccount = function _MultichainAccountWalletAdapter_createMultichainAccount(groupIndex) {
const multichainAccount = new MultichainAccountAdapter({
wallet: this,
providers: __classPrivateFieldGet(this, _MultichainAccountWalletAdapter_providers, "f"),
groupIndex,
});
// Register the account to our internal map.
__classPrivateFieldGet(this, _MultichainAccountWalletAdapter_accounts, "f").set(groupIndex, multichainAccount);
return multichainAccount;
};
//# sourceMappingURL=adapters.mjs.map