Skip to content

Commit 423d1ff

Browse files
committed
refactor: rely on account provider to construct multichain wallet and accounts
1 parent 640d895 commit 423d1ff

2 files changed

Lines changed: 328 additions & 156 deletions

File tree

packages/multichain-account-api/src/api.ts

Lines changed: 90 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ export type AccountType = string;
1414
export type AccountMethod = string;
1515

1616
export type AccountProvider = {
17+
getEntropySources: () => EntropySourceId[];
18+
19+
getAccounts: (opts: {
20+
entropySource: EntropySourceId;
21+
groupIndex: number;
22+
}) => InternalAccount[];
23+
1724
createAccounts: (opts: {
1825
entropySource: EntropySourceId;
1926
groupIndex: number;
@@ -39,7 +46,7 @@ export type MultichainAccountSelector = {
3946

4047
export type MultichainAccount = {
4148
get id(): MultichainAccountId;
42-
get walletId(): MultichainAccountWalletId;
49+
get wallet(): MultichainAccountWallet;
4350
get index(): number;
4451
get accounts(): InternalAccount[];
4552

@@ -98,33 +105,63 @@ export function toMultichainAccountId(
98105
export class MultichainAccountAdapter implements MultichainAccount {
99106
readonly #id: MultichainAccountId;
100107

101-
readonly #walletId: MultichainAccountWalletId;
108+
readonly #wallet: MultichainAccountWallet;
102109

103110
readonly #index: number;
104111

105-
readonly #accounts: InternalAccount[];
112+
readonly #providers: AccountProvider[];
113+
114+
#accounts: InternalAccount[];
106115

107116
constructor({
108-
accounts,
109117
groupIndex,
110-
walletId,
118+
wallet,
119+
providers,
111120
}: {
112-
accounts: InternalAccount[];
113121
groupIndex: number;
114-
walletId: MultichainAccountWalletId;
122+
wallet: MultichainAccountWallet;
123+
providers: AccountProvider[];
115124
}) {
116-
this.#id = toMultichainAccountId(walletId, groupIndex);
125+
this.#id = toMultichainAccountId(wallet.id, groupIndex);
117126
this.#index = groupIndex;
127+
this.#wallet = wallet;
128+
this.#providers = providers;
129+
this.#accounts = [];
130+
}
131+
132+
async init(): Promise<void> {
133+
let accounts: InternalAccount[] = [];
134+
135+
for (const provider of this.#providers) {
136+
accounts = accounts.concat(
137+
provider.getAccounts({
138+
entropySource: this.#wallet.entropySource,
139+
groupIndex: this.#index,
140+
}),
141+
);
142+
}
143+
118144
this.#accounts = accounts;
119-
this.#walletId = walletId;
145+
}
146+
147+
static async from(args: {
148+
groupIndex: number;
149+
wallet: MultichainAccountWallet;
150+
providers: AccountProvider[];
151+
}): Promise<MultichainAccount> {
152+
const multichainAccount = new MultichainAccountAdapter(args);
153+
154+
await multichainAccount.init();
155+
156+
return multichainAccount;
120157
}
121158

122159
get id(): MultichainAccountId {
123160
return this.#id;
124161
}
125162

126-
get walletId(): MultichainAccountWalletId {
127-
return this.#walletId;
163+
get wallet(): MultichainAccountWallet {
164+
return this.#wallet;
128165
}
129166

130167
get index(): number {
@@ -225,22 +262,47 @@ export class MultichainAccountWalletAdapter implements MultichainAccountWallet {
225262
constructor({
226263
providers,
227264
entropySource,
228-
multichainAccounts,
229265
}: {
230266
providers: AccountProvider[];
231267
entropySource: EntropySourceId;
232-
multichainAccounts: { [groupIndex: number]: MultichainAccount };
233268
}) {
234269
this.#id = toMultichainAccountWalletId(entropySource);
235270
this.#providers = providers;
236271
this.#entropySource = entropySource;
237272

238273
this.#accounts = new Map();
239-
for (const [groupIndex, multichainAccount] of Object.entries(
240-
multichainAccounts,
241-
)) {
242-
this.#accounts.set(groupIndex as unknown as number, multichainAccount);
243-
}
274+
}
275+
276+
async init(): Promise<void> {
277+
let index = 0;
278+
let hasAccounts = false;
279+
280+
do {
281+
// Make an explicit const copy of that value, to avoid unsafe reference.
282+
// See: https://eslint.org/docs/latest/rules/no-loop-func
283+
const groupIndex = index;
284+
285+
hasAccounts = this.#providers.some((provider) => {
286+
return Boolean(
287+
provider.getAccounts({
288+
entropySource: this.#entropySource,
289+
groupIndex,
290+
}).length,
291+
);
292+
});
293+
294+
if (hasAccounts) {
295+
const multichainAccount = await MultichainAccountAdapter.from({
296+
groupIndex,
297+
wallet: this,
298+
providers: this.#providers,
299+
});
300+
301+
this.#accounts.set(groupIndex, multichainAccount);
302+
}
303+
304+
index += 1;
305+
} while (hasAccounts);
244306
}
245307

246308
get id(): MultichainAccountWalletId {
@@ -255,14 +317,13 @@ export class MultichainAccountWalletAdapter implements MultichainAccountWallet {
255317
return Array.from(this.#accounts.values()); // TODO: Prevent copy here.
256318
}
257319

258-
#createMultichainAccount(
320+
async #createMultichainAccount(
259321
groupIndex: number,
260-
accounts: InternalAccount[],
261-
): MultichainAccount {
262-
const multichainAccount = new MultichainAccountAdapter({
263-
walletId: this.id,
322+
): Promise<MultichainAccount> {
323+
const multichainAccount = await MultichainAccountAdapter.from({
324+
wallet: this,
325+
providers: this.#providers,
264326
groupIndex,
265-
accounts,
266327
});
267328

268329
// Register the account to our internal map.
@@ -286,20 +347,17 @@ export class MultichainAccountWalletAdapter implements MultichainAccountWallet {
286347
);
287348
}
288349

289-
const accounts: InternalAccount[] = [];
290350
for (const provider of this.#providers) {
291-
for (const account of await provider.createAccounts({
351+
await provider.createAccounts({
292352
entropySource: this.#entropySource,
293353
groupIndex,
294-
})) {
295-
accounts.push(account);
296-
}
354+
});
297355
}
298356

299357
// Re-create and "refresh" the multichain account (we assume all account creations are
300358
// idempotent, so we should get the same accounts and potentially some new accounts (if
301359
// some account providers decide to return more of them this time).
302-
return this.#createMultichainAccount(groupIndex, accounts);
360+
return await this.#createMultichainAccount(groupIndex);
303361
}
304362

305363
async createNextMultichainAccount(): Promise<MultichainAccount> {
@@ -339,17 +397,15 @@ export class MultichainAccountWalletAdapter implements MultichainAccountWallet {
339397
// We only create missing accounts if one of the provider has discovered
340398
// and created accounts.
341399
for (const provider of missingProviders) {
342-
const missingAccounts = await provider.createAccounts({
400+
await provider.createAccounts({
343401
entropySource: this.#entropySource,
344402
groupIndex,
345403
});
346-
347-
accounts = accounts.concat(missingAccounts);
348404
}
349405

350406
// We've got all the accounts now, we can create our multichain account.
351407
multichainAccounts.push(
352-
this.#createMultichainAccount(groupIndex, accounts),
408+
await this.#createMultichainAccount(groupIndex),
353409
);
354410

355411
// We have accounts, we need to check the next index.

0 commit comments

Comments
 (0)