@xian-tech/wallet-core 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approvals.d.ts +1 -1
- package/dist/approvals.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/controller.d.ts +101 -2
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +546 -40
- package/dist/controller.js.map +1 -1
- package/dist/crypto.d.ts +2 -2
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +14 -2
- package/dist/crypto.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +59 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/controller.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Ed25519Signer, XianClient } from "@xian-tech/client";
|
|
2
2
|
import { ProviderChainMismatchError, ProviderUnauthorizedError, ProviderUnsupportedMethodError } from "@xian-tech/provider";
|
|
3
|
-
import { approvalKindFromMethod, buildApprovalView } from "./approvals";
|
|
4
|
-
import { DEFAULT_NETWORK_PRESETS, DEFAULT_DASHBOARD_URL, DEFAULT_RPC_URL, LOCAL_NETWORK_PRESET_NAME, DEFAULT_WALLET_CAPABILITIES, LOCAL_NETWORK_PRESET_ID } from "./constants";
|
|
5
|
-
import { createWalletSecret, decryptMnemonic, decryptPrivateKey, encryptMnemonic, encryptPrivateKey, isUnsafeMessageToSign } from "./crypto";
|
|
3
|
+
import { approvalKindFromMethod, buildApprovalView } from "./approvals.js";
|
|
4
|
+
import { DEFAULT_NETWORK_PRESETS, DEFAULT_DASHBOARD_URL, DEFAULT_RPC_URL, LOCAL_NETWORK_PRESET_NAME, DEFAULT_WALLET_CAPABILITIES, LOCAL_NETWORK_PRESET_ID, UNLOCKED_SESSION_TIMEOUT_MS } from "./constants.js";
|
|
5
|
+
import { createWalletSecret, decryptMnemonic, decryptPrivateKey, derivePrivateKeyFromMnemonic, encryptMnemonic, encryptPrivateKey, isUnsafeMessageToSign } from "./crypto.js";
|
|
6
|
+
const SAFE_CHAIN_ID_LOOKUP_TIMEOUT_MS = 2_000;
|
|
6
7
|
function firstParamObject(params) {
|
|
7
8
|
if (Array.isArray(params)) {
|
|
8
9
|
return (params[0] ?? {});
|
|
@@ -35,6 +36,15 @@ function trimOptionalString(value) {
|
|
|
35
36
|
const trimmed = value?.trim();
|
|
36
37
|
return trimmed ? trimmed : undefined;
|
|
37
38
|
}
|
|
39
|
+
function normalizeTrackedAsset(asset) {
|
|
40
|
+
return {
|
|
41
|
+
contract: asset.contract.trim(),
|
|
42
|
+
name: trimOptionalString(asset.name),
|
|
43
|
+
symbol: trimOptionalString(asset.symbol),
|
|
44
|
+
icon: trimOptionalString(asset.icon),
|
|
45
|
+
decimals: asset.decimals
|
|
46
|
+
};
|
|
47
|
+
}
|
|
38
48
|
function createLocalNetworkPreset() {
|
|
39
49
|
const preset = DEFAULT_NETWORK_PRESETS[0];
|
|
40
50
|
if (preset) {
|
|
@@ -168,13 +178,55 @@ export class WalletController {
|
|
|
168
178
|
message: String(error)
|
|
169
179
|
};
|
|
170
180
|
}
|
|
171
|
-
|
|
181
|
+
async restoreUnlockedSession() {
|
|
182
|
+
if (this.unlockedPrivateKey) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
const session = await this.store.loadUnlockedSession();
|
|
186
|
+
if (!session) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (session.expiresAt <= this.now()) {
|
|
190
|
+
await this.store.clearUnlockedSession();
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
this.unlockedPrivateKey = session.privateKey;
|
|
194
|
+
this.unlockedSigner = new Ed25519Signer(session.privateKey);
|
|
195
|
+
if (session.mnemonic) {
|
|
196
|
+
this.unlockedMnemonic = session.mnemonic;
|
|
197
|
+
}
|
|
198
|
+
if (session.password) {
|
|
199
|
+
this.unlockedPassword = session.password;
|
|
200
|
+
}
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
unlockedMnemonic = null;
|
|
204
|
+
unlockedPassword = null;
|
|
205
|
+
async persistUnlockedSession(privateKey, expiresAt = this.now() + UNLOCKED_SESSION_TIMEOUT_MS) {
|
|
206
|
+
const session = {
|
|
207
|
+
privateKey,
|
|
208
|
+
mnemonic: this.unlockedMnemonic ?? undefined,
|
|
209
|
+
password: this.unlockedPassword ?? undefined,
|
|
210
|
+
expiresAt
|
|
211
|
+
};
|
|
212
|
+
await this.store.saveUnlockedSession(session);
|
|
213
|
+
}
|
|
214
|
+
async clearUnlockedSession() {
|
|
215
|
+
this.unlockedPrivateKey = null;
|
|
216
|
+
this.unlockedSigner = null;
|
|
217
|
+
this.unlockedMnemonic = null;
|
|
218
|
+
this.unlockedPassword = null;
|
|
219
|
+
await this.store.clearUnlockedSession();
|
|
220
|
+
}
|
|
221
|
+
async getUnlockedSigner() {
|
|
222
|
+
await this.restoreUnlockedSession();
|
|
172
223
|
if (!this.unlockedPrivateKey) {
|
|
173
224
|
throw new ProviderUnauthorizedError("wallet is locked");
|
|
174
225
|
}
|
|
175
226
|
if (!this.unlockedSigner) {
|
|
176
227
|
this.unlockedSigner = new Ed25519Signer(this.unlockedPrivateKey);
|
|
177
228
|
}
|
|
229
|
+
await this.persistUnlockedSession(this.unlockedPrivateKey);
|
|
178
230
|
return this.unlockedSigner;
|
|
179
231
|
}
|
|
180
232
|
currentClient(state) {
|
|
@@ -255,12 +307,30 @@ export class WalletController {
|
|
|
255
307
|
return undefined;
|
|
256
308
|
}
|
|
257
309
|
try {
|
|
258
|
-
return await this.currentClient(state).getChainId();
|
|
310
|
+
return await this.withTimeout(this.currentClient(state).getChainId(), SAFE_CHAIN_ID_LOOKUP_TIMEOUT_MS);
|
|
259
311
|
}
|
|
260
312
|
catch {
|
|
261
313
|
return undefined;
|
|
262
314
|
}
|
|
263
315
|
}
|
|
316
|
+
async withTimeout(promise, timeoutMs) {
|
|
317
|
+
let timeoutId;
|
|
318
|
+
try {
|
|
319
|
+
return await Promise.race([
|
|
320
|
+
promise,
|
|
321
|
+
new Promise((_, reject) => {
|
|
322
|
+
timeoutId = globalThis.setTimeout(() => {
|
|
323
|
+
reject(new Error("operation timed out"));
|
|
324
|
+
}, timeoutMs);
|
|
325
|
+
})
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
if (timeoutId !== undefined) {
|
|
330
|
+
globalThis.clearTimeout(timeoutId);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
264
334
|
async buildWalletInfo(state, origin) {
|
|
265
335
|
if (!state) {
|
|
266
336
|
return {
|
|
@@ -272,7 +342,7 @@ export class WalletController {
|
|
|
272
342
|
};
|
|
273
343
|
}
|
|
274
344
|
const connected = state.connectedOrigins.includes(origin);
|
|
275
|
-
const unlocked = this.
|
|
345
|
+
const unlocked = await this.restoreUnlockedSession();
|
|
276
346
|
const preset = this.activeNetworkPreset(state);
|
|
277
347
|
const resolvedChainId = await this.safeGetChainId(state);
|
|
278
348
|
return {
|
|
@@ -312,6 +382,50 @@ export class WalletController {
|
|
|
312
382
|
watchedAssets: updater(state.watchedAssets)
|
|
313
383
|
});
|
|
314
384
|
}
|
|
385
|
+
async fetchDetectedAssets(state) {
|
|
386
|
+
if (!state) {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
const client = this.currentClient(state);
|
|
390
|
+
const trackedContracts = new Set(state.watchedAssets.map((asset) => asset.contract));
|
|
391
|
+
const detectedAssets = [];
|
|
392
|
+
const seenContracts = new Set();
|
|
393
|
+
const pageSize = 200;
|
|
394
|
+
let offset = 0;
|
|
395
|
+
while (true) {
|
|
396
|
+
const page = await client.getTokenBalances(state.publicKey, {
|
|
397
|
+
limit: pageSize,
|
|
398
|
+
offset
|
|
399
|
+
});
|
|
400
|
+
for (const item of page.items) {
|
|
401
|
+
const contract = item.contract.trim();
|
|
402
|
+
if (!contract || seenContracts.has(contract)) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
seenContracts.add(contract);
|
|
406
|
+
detectedAssets.push({
|
|
407
|
+
contract,
|
|
408
|
+
name: trimOptionalString(item.name ?? undefined),
|
|
409
|
+
symbol: trimOptionalString(item.symbol ?? undefined),
|
|
410
|
+
icon: trimOptionalString(item.logoUrl ?? undefined),
|
|
411
|
+
balance: item.balance,
|
|
412
|
+
tracked: trackedContracts.has(contract)
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
const fetched = page.items.length;
|
|
416
|
+
if (fetched === 0 || offset + fetched >= page.total) {
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
offset += fetched;
|
|
420
|
+
}
|
|
421
|
+
detectedAssets.sort((left, right) => {
|
|
422
|
+
if (left.tracked !== right.tracked) {
|
|
423
|
+
return left.tracked ? 1 : -1;
|
|
424
|
+
}
|
|
425
|
+
return left.contract.localeCompare(right.contract);
|
|
426
|
+
});
|
|
427
|
+
return detectedAssets;
|
|
428
|
+
}
|
|
315
429
|
sanitizeNetworkPresetInput(input) {
|
|
316
430
|
const name = input.name.trim();
|
|
317
431
|
const rpcUrl = input.rpcUrl.trim();
|
|
@@ -375,8 +489,45 @@ export class WalletController {
|
|
|
375
489
|
await this.broadcastProviderEvent("accountsChanged", [[]], origin);
|
|
376
490
|
await this.broadcastProviderEvent("disconnect", [{ code: 4100, message: "wallet disconnected" }], origin);
|
|
377
491
|
}
|
|
492
|
+
async notifyUnlockedOrigins(state) {
|
|
493
|
+
if (state.connectedOrigins.length === 0) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const chainId = this.displayChainId(this.activeNetworkPreset(state), await this.safeGetChainId(state)) ?? "unknown";
|
|
497
|
+
await Promise.allSettled(state.connectedOrigins.map((origin) => this.emitConnectionLifecycle(origin, chainId, state.publicKey)));
|
|
498
|
+
}
|
|
499
|
+
async emitSelectedAccountChangedForConnectedOrigins(state) {
|
|
500
|
+
if (state.connectedOrigins.length === 0) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (await this.restoreUnlockedSession()) {
|
|
504
|
+
await Promise.allSettled(state.connectedOrigins.map((origin) => this.broadcastProviderEvent("accountsChanged", [[state.publicKey]], origin)));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
await Promise.allSettled(state.connectedOrigins.map((origin) => this.emitDisconnectLifecycle(origin)));
|
|
508
|
+
}
|
|
509
|
+
async invalidatePendingRequests(reason) {
|
|
510
|
+
const requestStates = await this.store.listRequestStates();
|
|
511
|
+
const settledPendingRequestIds = new Set();
|
|
512
|
+
for (const requestState of requestStates) {
|
|
513
|
+
if (requestState.status !== "pending") {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
settledPendingRequestIds.add(requestState.requestId);
|
|
517
|
+
await this.rejectRequest(requestState, reason);
|
|
518
|
+
}
|
|
519
|
+
for (const [requestId, waiter] of this.requestWaiters.entries()) {
|
|
520
|
+
if (!settledPendingRequestIds.has(requestId)) {
|
|
521
|
+
waiter.reject(reason);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
this.requestWaiters.clear();
|
|
525
|
+
for (const approval of await this.store.listApprovalStates()) {
|
|
526
|
+
await this.store.deleteApprovalState(approval.id);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
378
529
|
async prepareTransaction(state, intent) {
|
|
379
|
-
const signer = this.getUnlockedSigner();
|
|
530
|
+
const signer = await this.getUnlockedSigner();
|
|
380
531
|
const client = this.currentClient(state);
|
|
381
532
|
const activeChainId = await client.getChainId();
|
|
382
533
|
if (intent.chainId && intent.chainId !== activeChainId) {
|
|
@@ -393,7 +544,7 @@ export class WalletController {
|
|
|
393
544
|
});
|
|
394
545
|
}
|
|
395
546
|
async signPreparedTransaction(state, tx) {
|
|
396
|
-
const signer = this.getUnlockedSigner();
|
|
547
|
+
const signer = await this.getUnlockedSigner();
|
|
397
548
|
const activeChainId = await this.currentClient(state).getChainId();
|
|
398
549
|
if (tx.payload.sender !== signer.address) {
|
|
399
550
|
throw new ProviderUnauthorizedError("transaction sender does not match the active wallet");
|
|
@@ -411,7 +562,7 @@ export class WalletController {
|
|
|
411
562
|
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
412
563
|
switch (request.method) {
|
|
413
564
|
case "xian_requestAccounts": {
|
|
414
|
-
this.getUnlockedSigner();
|
|
565
|
+
await this.getUnlockedSigner();
|
|
415
566
|
const chainId = this.displayChainId(this.activeNetworkPreset(state), await this.safeGetChainId(state));
|
|
416
567
|
const nextState = await this.updateConnectedOrigin(origin, true);
|
|
417
568
|
await this.emitConnectionLifecycle(origin, chainId ?? "unknown", nextState.publicKey);
|
|
@@ -419,9 +570,9 @@ export class WalletController {
|
|
|
419
570
|
}
|
|
420
571
|
case "xian_watchAsset": {
|
|
421
572
|
this.requireConnectedOrigin(state, origin);
|
|
422
|
-
this.getUnlockedSigner();
|
|
573
|
+
await this.getUnlockedSigner();
|
|
423
574
|
const assetRequest = firstParamObject(request.params);
|
|
424
|
-
const asset = assetRequest.options;
|
|
575
|
+
const asset = normalizeTrackedAsset(assetRequest.options);
|
|
425
576
|
await this.updateWatchedAssets((assets) => {
|
|
426
577
|
const next = assets.filter((entry) => entry.contract !== asset.contract);
|
|
427
578
|
next.push(asset);
|
|
@@ -431,7 +582,7 @@ export class WalletController {
|
|
|
431
582
|
}
|
|
432
583
|
case "xian_signMessage": {
|
|
433
584
|
this.requireConnectedOrigin(state, origin);
|
|
434
|
-
const signer = this.getUnlockedSigner();
|
|
585
|
+
const signer = await this.getUnlockedSigner();
|
|
435
586
|
const { message } = firstParamObject(request.params);
|
|
436
587
|
if (typeof message !== "string") {
|
|
437
588
|
throw new TypeError("xian_signMessage requires a message string");
|
|
@@ -443,13 +594,13 @@ export class WalletController {
|
|
|
443
594
|
}
|
|
444
595
|
case "xian_signTransaction": {
|
|
445
596
|
this.requireConnectedOrigin(state, origin);
|
|
446
|
-
this.getUnlockedSigner();
|
|
597
|
+
await this.getUnlockedSigner();
|
|
447
598
|
const { tx } = firstParamObject(request.params);
|
|
448
599
|
return this.signPreparedTransaction(state, tx);
|
|
449
600
|
}
|
|
450
601
|
case "xian_sendTransaction": {
|
|
451
602
|
this.requireConnectedOrigin(state, origin);
|
|
452
|
-
this.getUnlockedSigner();
|
|
603
|
+
await this.getUnlockedSigner();
|
|
453
604
|
const { tx, mode, waitForTx, timeoutMs, pollIntervalMs } = firstParamObject(request.params);
|
|
454
605
|
return this.sendPreparedTransaction(state, tx, {
|
|
455
606
|
mode: mode,
|
|
@@ -460,7 +611,7 @@ export class WalletController {
|
|
|
460
611
|
}
|
|
461
612
|
case "xian_sendCall": {
|
|
462
613
|
this.requireConnectedOrigin(state, origin);
|
|
463
|
-
this.getUnlockedSigner();
|
|
614
|
+
await this.getUnlockedSigner();
|
|
464
615
|
const { intent, mode, waitForTx, timeoutMs, pollIntervalMs } = firstParamObject(request.params);
|
|
465
616
|
const tx = await this.prepareTransaction(state, intent);
|
|
466
617
|
return this.sendPreparedTransaction(state, tx, {
|
|
@@ -563,7 +714,7 @@ export class WalletController {
|
|
|
563
714
|
};
|
|
564
715
|
case "xian_requestAccounts": {
|
|
565
716
|
const walletState = this.requireStoredWallet(state);
|
|
566
|
-
this.getUnlockedSigner();
|
|
717
|
+
await this.getUnlockedSigner();
|
|
567
718
|
const approvalChainId = this.displayChainId(this.activeNetworkPreset(walletState), await this.safeGetChainId(walletState));
|
|
568
719
|
if (walletState.connectedOrigins.includes(origin)) {
|
|
569
720
|
return {
|
|
@@ -592,9 +743,7 @@ export class WalletController {
|
|
|
592
743
|
};
|
|
593
744
|
}
|
|
594
745
|
case "xian_accounts":
|
|
595
|
-
if (!state ||
|
|
596
|
-
this.unlockedPrivateKey == null ||
|
|
597
|
-
!state.connectedOrigins.includes(origin)) {
|
|
746
|
+
if (!state || !(await this.restoreUnlockedSession()) || !state.connectedOrigins.includes(origin)) {
|
|
598
747
|
return {
|
|
599
748
|
kind: "result",
|
|
600
749
|
value: []
|
|
@@ -640,7 +789,7 @@ export class WalletController {
|
|
|
640
789
|
case "xian_watchAsset": {
|
|
641
790
|
const walletState = this.requireStoredWallet(state);
|
|
642
791
|
this.requireConnectedOrigin(walletState, origin);
|
|
643
|
-
this.getUnlockedSigner();
|
|
792
|
+
await this.getUnlockedSigner();
|
|
644
793
|
return {
|
|
645
794
|
kind: "approval",
|
|
646
795
|
account: walletState.publicKey,
|
|
@@ -650,7 +799,7 @@ export class WalletController {
|
|
|
650
799
|
case "xian_signMessage": {
|
|
651
800
|
const walletState = this.requireStoredWallet(state);
|
|
652
801
|
this.requireConnectedOrigin(walletState, origin);
|
|
653
|
-
this.getUnlockedSigner();
|
|
802
|
+
await this.getUnlockedSigner();
|
|
654
803
|
return {
|
|
655
804
|
kind: "approval",
|
|
656
805
|
account: walletState.publicKey,
|
|
@@ -660,7 +809,7 @@ export class WalletController {
|
|
|
660
809
|
case "xian_prepareTransaction": {
|
|
661
810
|
const walletState = this.requireStoredWallet(state);
|
|
662
811
|
this.requireConnectedOrigin(walletState, origin);
|
|
663
|
-
this.getUnlockedSigner();
|
|
812
|
+
await this.getUnlockedSigner();
|
|
664
813
|
const { intent } = firstParamObject(request.params);
|
|
665
814
|
return {
|
|
666
815
|
kind: "result",
|
|
@@ -672,7 +821,7 @@ export class WalletController {
|
|
|
672
821
|
case "xian_sendCall": {
|
|
673
822
|
const walletState = this.requireStoredWallet(state);
|
|
674
823
|
this.requireConnectedOrigin(walletState, origin);
|
|
675
|
-
this.getUnlockedSigner();
|
|
824
|
+
await this.getUnlockedSigner();
|
|
676
825
|
return {
|
|
677
826
|
kind: "approval",
|
|
678
827
|
account: walletState.publicKey,
|
|
@@ -683,6 +832,13 @@ export class WalletController {
|
|
|
683
832
|
throw new ProviderUnsupportedMethodError(request.method);
|
|
684
833
|
}
|
|
685
834
|
}
|
|
835
|
+
getAccountsList(state) {
|
|
836
|
+
if (state.accounts && state.accounts.length > 0) {
|
|
837
|
+
return state.accounts.map((a) => ({ index: a.index, publicKey: a.publicKey, name: a.name }));
|
|
838
|
+
}
|
|
839
|
+
// Backward compat: create virtual account from legacy fields
|
|
840
|
+
return [{ index: 0, publicKey: state.publicKey, name: "Account 1" }];
|
|
841
|
+
}
|
|
686
842
|
async getPopupState() {
|
|
687
843
|
const state = await this.loadWalletState();
|
|
688
844
|
const approvals = await this.store.listApprovalStates();
|
|
@@ -691,9 +847,11 @@ export class WalletController {
|
|
|
691
847
|
.sort((left, right) => right.createdAt - left.createdAt);
|
|
692
848
|
const activePreset = state ? this.activeNetworkPreset(state) : undefined;
|
|
693
849
|
const resolvedChainId = await this.safeGetChainId(state);
|
|
850
|
+
const unlocked = await this.restoreUnlockedSession();
|
|
851
|
+
const watchedAssets = state?.watchedAssets ?? [];
|
|
694
852
|
return {
|
|
695
853
|
hasWallet: state != null,
|
|
696
|
-
unlocked
|
|
854
|
+
unlocked,
|
|
697
855
|
publicKey: state?.publicKey,
|
|
698
856
|
rpcUrl: state?.rpcUrl ?? DEFAULT_RPC_URL,
|
|
699
857
|
dashboardUrl: state?.dashboardUrl ?? DEFAULT_DASHBOARD_URL,
|
|
@@ -708,16 +866,133 @@ export class WalletController {
|
|
|
708
866
|
activeNetworkId: activePreset?.id,
|
|
709
867
|
activeNetworkName: activePreset?.name,
|
|
710
868
|
networkPresets: state?.networkPresets ?? DEFAULT_NETWORK_PRESETS,
|
|
711
|
-
watchedAssets
|
|
869
|
+
watchedAssets,
|
|
870
|
+
detectedAssets: [],
|
|
871
|
+
assetBalances: {},
|
|
872
|
+
assetFiatValues: {},
|
|
712
873
|
connectedOrigins: state?.connectedOrigins ?? [],
|
|
713
874
|
pendingApprovalCount: pendingApprovals.length,
|
|
714
875
|
pendingApprovals,
|
|
715
876
|
hasRecoveryPhrase: Boolean(state?.encryptedMnemonic),
|
|
716
877
|
seedSource: state?.seedSource,
|
|
717
878
|
mnemonicWordCount: state?.mnemonicWordCount,
|
|
879
|
+
accounts: state ? this.getAccountsList(state) : [],
|
|
880
|
+
activeAccountIndex: state?.activeAccountIndex ?? 0,
|
|
718
881
|
version: this.options.version
|
|
719
882
|
};
|
|
720
883
|
}
|
|
884
|
+
async getAssetBalances() {
|
|
885
|
+
const state = await this.loadWalletState();
|
|
886
|
+
if (!state) {
|
|
887
|
+
return {};
|
|
888
|
+
}
|
|
889
|
+
return this.fetchAssetBalances(state, state.watchedAssets);
|
|
890
|
+
}
|
|
891
|
+
async getTokenMetadata(contract) {
|
|
892
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
893
|
+
return this.currentClient(state).getTokenMetadata(contract);
|
|
894
|
+
}
|
|
895
|
+
async getDetectedAssets() {
|
|
896
|
+
const state = await this.loadWalletState();
|
|
897
|
+
if (!state) {
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
900
|
+
try {
|
|
901
|
+
return await this.fetchDetectedAssets(state);
|
|
902
|
+
}
|
|
903
|
+
catch {
|
|
904
|
+
return [];
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
async trackAsset(asset) {
|
|
908
|
+
const normalized = normalizeTrackedAsset(asset);
|
|
909
|
+
if (!normalized.contract) {
|
|
910
|
+
throw new TypeError("asset contract is required");
|
|
911
|
+
}
|
|
912
|
+
await this.updateWatchedAssets((assets) => {
|
|
913
|
+
const next = assets.filter((entry) => entry.contract !== normalized.contract);
|
|
914
|
+
next.push(normalized);
|
|
915
|
+
return next;
|
|
916
|
+
});
|
|
917
|
+
return this.getPopupState();
|
|
918
|
+
}
|
|
919
|
+
async updateAssetSettings(assets) {
|
|
920
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
921
|
+
for (const update of assets) {
|
|
922
|
+
const asset = state.watchedAssets.find((a) => a.contract === update.contract);
|
|
923
|
+
if (asset) {
|
|
924
|
+
if (update.hidden !== undefined) {
|
|
925
|
+
asset.hidden = update.hidden;
|
|
926
|
+
}
|
|
927
|
+
if (update.order !== undefined) {
|
|
928
|
+
asset.order = update.order;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
await this.store.saveState(state);
|
|
933
|
+
return this.getPopupState();
|
|
934
|
+
}
|
|
935
|
+
async updateWatchedAssetDecimals(contract, decimals) {
|
|
936
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
937
|
+
const idx = state.watchedAssets.findIndex((asset) => asset.contract === contract);
|
|
938
|
+
if (idx === -1) {
|
|
939
|
+
throw new Error(`asset ${contract} is not watched`);
|
|
940
|
+
}
|
|
941
|
+
const existing = state.watchedAssets[idx];
|
|
942
|
+
state.watchedAssets[idx] = { ...existing, decimals };
|
|
943
|
+
await this.store.saveState(state);
|
|
944
|
+
return this.getPopupState();
|
|
945
|
+
}
|
|
946
|
+
async estimateTransactionStamps(request) {
|
|
947
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
948
|
+
const client = this.currentClient(state);
|
|
949
|
+
return client.estimateStamps({
|
|
950
|
+
sender: state.publicKey,
|
|
951
|
+
contract: request.contract,
|
|
952
|
+
function: request.function,
|
|
953
|
+
kwargs: request.kwargs
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
async sendDirectTransaction(intent) {
|
|
957
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
958
|
+
await this.getUnlockedSigner();
|
|
959
|
+
const tx = await this.prepareTransaction(state, {
|
|
960
|
+
contract: intent.contract,
|
|
961
|
+
function: intent.function,
|
|
962
|
+
kwargs: intent.kwargs,
|
|
963
|
+
stamps: intent.stamps
|
|
964
|
+
});
|
|
965
|
+
return this.sendPreparedTransaction(state, tx, { mode: "commit" });
|
|
966
|
+
}
|
|
967
|
+
async getContractMethods(contract) {
|
|
968
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
969
|
+
return this.currentClient(state).getContractMethods(contract);
|
|
970
|
+
}
|
|
971
|
+
async fetchAssetBalances(state, assets) {
|
|
972
|
+
const balances = {};
|
|
973
|
+
if (!state || assets.length === 0) {
|
|
974
|
+
return balances;
|
|
975
|
+
}
|
|
976
|
+
const client = this.currentClient(state);
|
|
977
|
+
const results = await Promise.allSettled(assets.map(async (asset) => {
|
|
978
|
+
const raw = await client.getBalance(state.publicKey, {
|
|
979
|
+
contract: asset.contract
|
|
980
|
+
});
|
|
981
|
+
return { contract: asset.contract, raw };
|
|
982
|
+
}));
|
|
983
|
+
for (const result of results) {
|
|
984
|
+
if (result.status === "fulfilled") {
|
|
985
|
+
const { contract, raw } = result.value;
|
|
986
|
+
balances[contract] =
|
|
987
|
+
raw != null ? String(raw) : null;
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
// If a single balance fetch fails, mark it null rather than
|
|
991
|
+
// failing the entire popup state.
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return balances;
|
|
995
|
+
}
|
|
721
996
|
async createOrImportWallet(input) {
|
|
722
997
|
const secret = await createWalletSecret({
|
|
723
998
|
privateKey: input.privateKey,
|
|
@@ -731,17 +1006,10 @@ export class WalletController {
|
|
|
731
1006
|
: undefined;
|
|
732
1007
|
this.unlockedPrivateKey = secret.privateKey;
|
|
733
1008
|
this.unlockedSigner = signer;
|
|
734
|
-
|
|
735
|
-
this.
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
for (const requestState of await this.store.listRequestStates()) {
|
|
740
|
-
await this.store.deleteRequestState(requestState.requestId);
|
|
741
|
-
}
|
|
742
|
-
for (const approval of await this.store.listApprovalStates()) {
|
|
743
|
-
await this.store.deleteApprovalState(approval.id);
|
|
744
|
-
}
|
|
1009
|
+
this.unlockedMnemonic = secret.mnemonic ?? null;
|
|
1010
|
+
this.unlockedPassword = input.password;
|
|
1011
|
+
await this.persistUnlockedSession(secret.privateKey);
|
|
1012
|
+
await this.invalidatePendingRequests(new ProviderUnauthorizedError("wallet was replaced"));
|
|
745
1013
|
const setupRpcUrl = trimOptionalString(input.rpcUrl) ?? DEFAULT_RPC_URL;
|
|
746
1014
|
const setupDashboardUrl = trimOptionalString(input.dashboardUrl) ?? DEFAULT_DASHBOARD_URL;
|
|
747
1015
|
const localPreset = createLocalNetworkPreset();
|
|
@@ -767,12 +1035,20 @@ export class WalletController {
|
|
|
767
1035
|
const networkPresets = useLocalPreset
|
|
768
1036
|
? [localPreset]
|
|
769
1037
|
: [localPreset, activePreset];
|
|
1038
|
+
const initialAccount = {
|
|
1039
|
+
index: 0,
|
|
1040
|
+
publicKey: signer.address,
|
|
1041
|
+
encryptedPrivateKey,
|
|
1042
|
+
name: "Account 1"
|
|
1043
|
+
};
|
|
770
1044
|
const popupState = await this.persistWalletState({
|
|
771
1045
|
publicKey: signer.address,
|
|
772
1046
|
encryptedPrivateKey,
|
|
773
1047
|
encryptedMnemonic,
|
|
774
1048
|
seedSource: secret.seedSource,
|
|
775
1049
|
mnemonicWordCount: secret.mnemonicWordCount,
|
|
1050
|
+
accounts: [initialAccount],
|
|
1051
|
+
activeAccountIndex: 0,
|
|
776
1052
|
rpcUrl: activePreset.rpcUrl,
|
|
777
1053
|
dashboardUrl: activePreset.dashboardUrl,
|
|
778
1054
|
activeNetworkId: activePreset.id,
|
|
@@ -802,8 +1078,18 @@ export class WalletController {
|
|
|
802
1078
|
}
|
|
803
1079
|
this.unlockedPrivateKey = privateKey;
|
|
804
1080
|
this.unlockedSigner = signer;
|
|
805
|
-
|
|
806
|
-
|
|
1081
|
+
this.unlockedPassword = password;
|
|
1082
|
+
// Decrypt mnemonic into session for account switching
|
|
1083
|
+
if (state.encryptedMnemonic) {
|
|
1084
|
+
try {
|
|
1085
|
+
this.unlockedMnemonic = await decryptMnemonic(state.encryptedMnemonic, password);
|
|
1086
|
+
}
|
|
1087
|
+
catch {
|
|
1088
|
+
this.unlockedMnemonic = null;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
await this.persistUnlockedSession(privateKey);
|
|
1092
|
+
void this.notifyUnlockedOrigins(state);
|
|
807
1093
|
return this.getPopupState();
|
|
808
1094
|
}
|
|
809
1095
|
async revealMnemonic(password) {
|
|
@@ -813,13 +1099,233 @@ export class WalletController {
|
|
|
813
1099
|
}
|
|
814
1100
|
return decryptMnemonic(state.encryptedMnemonic, password);
|
|
815
1101
|
}
|
|
1102
|
+
async revealPrivateKey(password) {
|
|
1103
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1104
|
+
return decryptPrivateKey(state.encryptedPrivateKey, password);
|
|
1105
|
+
}
|
|
1106
|
+
async exportWallet(password) {
|
|
1107
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1108
|
+
const accounts = state.accounts ?? [
|
|
1109
|
+
{ index: 0, publicKey: state.publicKey, encryptedPrivateKey: state.encryptedPrivateKey, name: "Account 1" }
|
|
1110
|
+
];
|
|
1111
|
+
const backup = {
|
|
1112
|
+
version: 1,
|
|
1113
|
+
type: state.seedSource,
|
|
1114
|
+
accounts: accounts.map((a) => ({ index: a.index, name: a.name })),
|
|
1115
|
+
activeAccountIndex: state.activeAccountIndex ?? accounts[0]?.index ?? 0,
|
|
1116
|
+
activeNetworkId: state.activeNetworkId,
|
|
1117
|
+
networkPresets: state.networkPresets.filter((p) => !p.builtin),
|
|
1118
|
+
watchedAssets: state.watchedAssets
|
|
1119
|
+
};
|
|
1120
|
+
if (state.encryptedMnemonic) {
|
|
1121
|
+
backup.mnemonic = await decryptMnemonic(state.encryptedMnemonic, password);
|
|
1122
|
+
}
|
|
1123
|
+
else {
|
|
1124
|
+
backup.privateKey = await decryptPrivateKey(state.encryptedPrivateKey, password);
|
|
1125
|
+
}
|
|
1126
|
+
return backup;
|
|
1127
|
+
}
|
|
1128
|
+
async importWalletBackup(backup, password) {
|
|
1129
|
+
// Derive or use the provided private key
|
|
1130
|
+
let primaryKey;
|
|
1131
|
+
let mnemonic;
|
|
1132
|
+
if (backup.type === "mnemonic" && backup.mnemonic) {
|
|
1133
|
+
mnemonic = backup.mnemonic;
|
|
1134
|
+
primaryKey = await derivePrivateKeyFromMnemonic(mnemonic, 0);
|
|
1135
|
+
}
|
|
1136
|
+
else if (backup.privateKey) {
|
|
1137
|
+
primaryKey = backup.privateKey;
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
throw new Error("backup must contain a mnemonic or private key");
|
|
1141
|
+
}
|
|
1142
|
+
const encryptedMnemonic = mnemonic
|
|
1143
|
+
? await encryptMnemonic(mnemonic, password)
|
|
1144
|
+
: undefined;
|
|
1145
|
+
// Build accounts list
|
|
1146
|
+
const accountEntries = backup.accounts ?? [{ index: 0, name: "Account 1" }];
|
|
1147
|
+
const accounts = [];
|
|
1148
|
+
const privateKeysByIndex = new Map();
|
|
1149
|
+
for (const entry of accountEntries) {
|
|
1150
|
+
const key = mnemonic
|
|
1151
|
+
? await derivePrivateKeyFromMnemonic(mnemonic, entry.index)
|
|
1152
|
+
: primaryKey;
|
|
1153
|
+
privateKeysByIndex.set(entry.index, key);
|
|
1154
|
+
const acctSigner = new Ed25519Signer(key);
|
|
1155
|
+
accounts.push({
|
|
1156
|
+
index: entry.index,
|
|
1157
|
+
publicKey: acctSigner.address,
|
|
1158
|
+
encryptedPrivateKey: await encryptPrivateKey(key, password),
|
|
1159
|
+
name: entry.name
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
const activeAccount = accounts.find((account) => account.index === backup.activeAccountIndex) ??
|
|
1163
|
+
accounts[0];
|
|
1164
|
+
if (!activeAccount) {
|
|
1165
|
+
throw new Error("backup must contain at least one account");
|
|
1166
|
+
}
|
|
1167
|
+
const activePrivateKey = privateKeysByIndex.get(activeAccount.index) ?? primaryKey;
|
|
1168
|
+
const signer = new Ed25519Signer(activePrivateKey);
|
|
1169
|
+
this.unlockedPrivateKey = activePrivateKey;
|
|
1170
|
+
this.unlockedSigner = signer;
|
|
1171
|
+
this.unlockedMnemonic = mnemonic ?? null;
|
|
1172
|
+
this.unlockedPassword = password;
|
|
1173
|
+
await this.persistUnlockedSession(activePrivateKey);
|
|
1174
|
+
await this.invalidatePendingRequests(new ProviderUnauthorizedError("wallet was replaced"));
|
|
1175
|
+
// Merge network presets
|
|
1176
|
+
const presets = [...DEFAULT_NETWORK_PRESETS];
|
|
1177
|
+
for (const p of backup.networkPresets ?? []) {
|
|
1178
|
+
if (!presets.some((existing) => existing.id === p.id)) {
|
|
1179
|
+
presets.push(p);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const activePreset = presets.find((preset) => preset.id === backup.activeNetworkId) ??
|
|
1183
|
+
presets[0];
|
|
1184
|
+
if (!activePreset) {
|
|
1185
|
+
throw new Error("backup must contain at least one network preset");
|
|
1186
|
+
}
|
|
1187
|
+
const watchedAssets = backup.watchedAssets?.length
|
|
1188
|
+
? backup.watchedAssets
|
|
1189
|
+
: [{ contract: "currency", name: "Xian", symbol: "XIAN" }];
|
|
1190
|
+
await this.persistWalletState({
|
|
1191
|
+
publicKey: activeAccount.publicKey,
|
|
1192
|
+
encryptedPrivateKey: activeAccount.encryptedPrivateKey,
|
|
1193
|
+
encryptedMnemonic,
|
|
1194
|
+
seedSource: backup.type,
|
|
1195
|
+
mnemonicWordCount: mnemonic ? mnemonic.split(" ").length : undefined,
|
|
1196
|
+
accounts,
|
|
1197
|
+
activeAccountIndex: activeAccount.index,
|
|
1198
|
+
rpcUrl: activePreset.rpcUrl,
|
|
1199
|
+
dashboardUrl: activePreset.dashboardUrl,
|
|
1200
|
+
activeNetworkId: activePreset.id,
|
|
1201
|
+
networkPresets: presets,
|
|
1202
|
+
watchedAssets,
|
|
1203
|
+
connectedOrigins: [],
|
|
1204
|
+
createdAt: new Date().toISOString()
|
|
1205
|
+
});
|
|
1206
|
+
return this.getPopupState();
|
|
1207
|
+
}
|
|
1208
|
+
async addAccount() {
|
|
1209
|
+
await this.restoreUnlockedSession();
|
|
1210
|
+
if (!this.unlockedMnemonic || !this.unlockedPassword) {
|
|
1211
|
+
throw new Error("wallet must be unlocked to add an account");
|
|
1212
|
+
}
|
|
1213
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1214
|
+
const accounts = state.accounts ?? [
|
|
1215
|
+
{ index: 0, publicKey: state.publicKey, encryptedPrivateKey: state.encryptedPrivateKey, name: "Account 1" }
|
|
1216
|
+
];
|
|
1217
|
+
const nextIndex = Math.max(...accounts.map((a) => a.index)) + 1;
|
|
1218
|
+
const privateKey = await derivePrivateKeyFromMnemonic(this.unlockedMnemonic, nextIndex);
|
|
1219
|
+
const signer = new Ed25519Signer(privateKey);
|
|
1220
|
+
const encrypted = await encryptPrivateKey(privateKey, this.unlockedPassword);
|
|
1221
|
+
accounts.push({
|
|
1222
|
+
index: nextIndex,
|
|
1223
|
+
publicKey: signer.address,
|
|
1224
|
+
encryptedPrivateKey: encrypted,
|
|
1225
|
+
name: `Account ${accounts.length + 1}`
|
|
1226
|
+
});
|
|
1227
|
+
state.publicKey = signer.address;
|
|
1228
|
+
state.encryptedPrivateKey = encrypted;
|
|
1229
|
+
state.activeAccountIndex = nextIndex;
|
|
1230
|
+
state.accounts = accounts;
|
|
1231
|
+
await this.store.saveState(state);
|
|
1232
|
+
this.unlockedPrivateKey = privateKey;
|
|
1233
|
+
this.unlockedSigner = signer;
|
|
1234
|
+
await this.persistUnlockedSession(privateKey);
|
|
1235
|
+
await this.emitSelectedAccountChangedForConnectedOrigins(state);
|
|
1236
|
+
return this.getPopupState();
|
|
1237
|
+
}
|
|
1238
|
+
async switchAccount(index) {
|
|
1239
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1240
|
+
const accounts = state.accounts ?? [
|
|
1241
|
+
{ index: 0, publicKey: state.publicKey, encryptedPrivateKey: state.encryptedPrivateKey, name: "Account 1" }
|
|
1242
|
+
];
|
|
1243
|
+
const target = accounts.find((a) => a.index === index);
|
|
1244
|
+
if (!target) {
|
|
1245
|
+
throw new Error("account not found");
|
|
1246
|
+
}
|
|
1247
|
+
// Update active account in state
|
|
1248
|
+
state.publicKey = target.publicKey;
|
|
1249
|
+
state.encryptedPrivateKey = target.encryptedPrivateKey;
|
|
1250
|
+
state.activeAccountIndex = index;
|
|
1251
|
+
await this.store.saveState(state);
|
|
1252
|
+
// If unlocked, switch the in-memory signer
|
|
1253
|
+
if (this.unlockedMnemonic) {
|
|
1254
|
+
const privateKey = await derivePrivateKeyFromMnemonic(this.unlockedMnemonic, index);
|
|
1255
|
+
this.unlockedPrivateKey = privateKey;
|
|
1256
|
+
this.unlockedSigner = new Ed25519Signer(privateKey);
|
|
1257
|
+
await this.persistUnlockedSession(privateKey);
|
|
1258
|
+
}
|
|
1259
|
+
else if (this.unlockedPrivateKey) {
|
|
1260
|
+
// No mnemonic in session — clear unlock (requires re-auth)
|
|
1261
|
+
await this.clearUnlockedSession();
|
|
1262
|
+
}
|
|
1263
|
+
await this.emitSelectedAccountChangedForConnectedOrigins(state);
|
|
1264
|
+
return this.getPopupState();
|
|
1265
|
+
}
|
|
1266
|
+
async renameAccount(index, name) {
|
|
1267
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1268
|
+
const accounts = state.accounts ?? [
|
|
1269
|
+
{ index: 0, publicKey: state.publicKey, encryptedPrivateKey: state.encryptedPrivateKey, name: "Account 1" }
|
|
1270
|
+
];
|
|
1271
|
+
const target = accounts.find((a) => a.index === index);
|
|
1272
|
+
if (!target) {
|
|
1273
|
+
throw new Error("account not found");
|
|
1274
|
+
}
|
|
1275
|
+
target.name = name;
|
|
1276
|
+
state.accounts = accounts;
|
|
1277
|
+
await this.store.saveState(state);
|
|
1278
|
+
return this.getPopupState();
|
|
1279
|
+
}
|
|
1280
|
+
async removeAccount(index) {
|
|
1281
|
+
const state = this.requireStoredWallet(await this.loadWalletState());
|
|
1282
|
+
if (index === 0) {
|
|
1283
|
+
throw new Error("cannot remove the primary account");
|
|
1284
|
+
}
|
|
1285
|
+
const accounts = state.accounts ?? [];
|
|
1286
|
+
const nextAccounts = accounts.filter((account) => account.index !== index);
|
|
1287
|
+
if (nextAccounts.length === 0) {
|
|
1288
|
+
throw new Error("cannot remove the last remaining account");
|
|
1289
|
+
}
|
|
1290
|
+
const removedActiveAccount = state.activeAccountIndex === index;
|
|
1291
|
+
state.accounts = nextAccounts;
|
|
1292
|
+
if (removedActiveAccount) {
|
|
1293
|
+
const nextActiveAccount = nextAccounts[0];
|
|
1294
|
+
state.publicKey = nextActiveAccount.publicKey;
|
|
1295
|
+
state.encryptedPrivateKey = nextActiveAccount.encryptedPrivateKey;
|
|
1296
|
+
state.activeAccountIndex = nextActiveAccount.index;
|
|
1297
|
+
if (this.unlockedMnemonic) {
|
|
1298
|
+
const privateKey = await derivePrivateKeyFromMnemonic(this.unlockedMnemonic, nextActiveAccount.index);
|
|
1299
|
+
this.unlockedPrivateKey = privateKey;
|
|
1300
|
+
this.unlockedSigner = new Ed25519Signer(privateKey);
|
|
1301
|
+
await this.persistUnlockedSession(privateKey);
|
|
1302
|
+
}
|
|
1303
|
+
else if (this.unlockedPrivateKey) {
|
|
1304
|
+
await this.clearUnlockedSession();
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
await this.store.saveState(state);
|
|
1308
|
+
if (removedActiveAccount) {
|
|
1309
|
+
await this.emitSelectedAccountChangedForConnectedOrigins(state);
|
|
1310
|
+
}
|
|
1311
|
+
return this.getPopupState();
|
|
1312
|
+
}
|
|
816
1313
|
async lockWallet() {
|
|
817
1314
|
const state = await this.loadWalletState();
|
|
818
|
-
this.
|
|
819
|
-
|
|
1315
|
+
await this.clearUnlockedSession();
|
|
1316
|
+
if (state) {
|
|
1317
|
+
await Promise.all(state.connectedOrigins.map((origin) => this.emitDisconnectLifecycle(origin)));
|
|
1318
|
+
}
|
|
1319
|
+
return this.getPopupState();
|
|
1320
|
+
}
|
|
1321
|
+
async removeWallet() {
|
|
1322
|
+
const state = await this.loadWalletState();
|
|
1323
|
+
await this.clearUnlockedSession();
|
|
820
1324
|
if (state) {
|
|
821
1325
|
await Promise.all(state.connectedOrigins.map((origin) => this.emitDisconnectLifecycle(origin)));
|
|
822
1326
|
}
|
|
1327
|
+
await this.invalidatePendingRequests(new ProviderUnauthorizedError("wallet was removed"));
|
|
1328
|
+
await this.store.clearState();
|
|
823
1329
|
return this.getPopupState();
|
|
824
1330
|
}
|
|
825
1331
|
async updateSettings(input) {
|