@metamask-previews/eth-snap-keyring 20.0.0-018fb62 → 20.0.0-c26e419

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/CHANGELOG.md CHANGED
@@ -7,14 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ### Added
11
-
12
- - Add `SnapKeyringV2` class, a per-snap keyring wrapper with O(1) account lookups by ID and address ([#501](https://github.com/MetaMask/accounts/pull/501))
13
-
14
10
  ### Changed
15
11
 
16
- - Refactor `SnapKeyring` to store accounts in per-snap `SnapKeyringV2` wrappers instead of a single flat map ([#501](https://github.com/MetaMask/accounts/pull/501))
17
- - Bump `@metamask/keyring-sdk` from none to `^1.1.0`
18
12
  - Bump `@metamask/messenger` from `^0.3.0` to `^1.1.1` ([#489](https://github.com/MetaMask/accounts/pull/489), [#500](https://github.com/MetaMask/accounts/pull/500))
19
13
  - Bump `@metamask/snaps-controllers` from `^19.0.0` to `^19.0.1` ([#500](https://github.com/MetaMask/accounts/pull/500))
20
14
  - Bump `@metamask/snaps-utils` from `^12.1.2` to `^12.1.3` ([#500](https://github.com/MetaMask/accounts/pull/500))
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  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");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _SnapKeyring_instances, _SnapKeyring_messenger, _SnapKeyring_snapClient, _SnapKeyring_snapKeyrings, _SnapKeyring_accountIndex, _SnapKeyring_selectedAccounts, _SnapKeyring_requests, _SnapKeyring_options, _SnapKeyring_callbacks, _SnapKeyring_isAnyAccountTypeAllowed, _SnapKeyring_lock, _SnapKeyring_withLock, _SnapKeyring_getOrCreateKeyringV2, _SnapKeyring_isMinimumPlatformVersion, _SnapKeyring_assertAccountCanBeUsed, _SnapKeyring_getExistingAccount, _SnapKeyring_getInternalOptions, _SnapKeyring_handleAccountCreated, _SnapKeyring_handleAccountUpdated, _SnapKeyring_handleAccountDeleted, _SnapKeyring_handleGetSelectedAccounts, _SnapKeyring_handleRequestApproved, _SnapKeyring_handleRequestRejected, _SnapKeyring_rePublishAccountEvent, _SnapKeyring_handleAccountBalancesUpdated, _SnapKeyring_handleAccountAssetListUpdated, _SnapKeyring_handleAccountTransactionsUpdated, _SnapKeyring_getAccount, _SnapKeyring_submitRequest, _SnapKeyring_submitSnapRequest, _SnapKeyring_hasMethod, _SnapKeyring_createRequestPromise, _SnapKeyring_clearRequestPromise, _SnapKeyring_handleSyncResponse, _SnapKeyring_handleAsyncResponse, _SnapKeyring_sanitizeRedirectUrl, _SnapKeyring_validateRedirectUrl, _SnapKeyring_deleteAccount, _SnapKeyring_resolveAddress, _SnapKeyring_updateSelectedAccountsMap, _SnapKeyring_getSnap, _SnapKeyring_getSnapMetadata, _SnapKeyring_getSnapAllowedOrigins, _SnapKeyring_transformToInternalAccount;
13
+ var _SnapKeyring_instances, _SnapKeyring_messenger, _SnapKeyring_snapClient, _SnapKeyring_accounts, _SnapKeyring_selectedAccounts, _SnapKeyring_requests, _SnapKeyring_options, _SnapKeyring_callbacks, _SnapKeyring_isAnyAccountTypeAllowed, _SnapKeyring_lock, _SnapKeyring_withLock, _SnapKeyring_isMinimumPlatformVersion, _SnapKeyring_assertAccountCanBeUsed, _SnapKeyring_getExistingAccount, _SnapKeyring_getInternalOptions, _SnapKeyring_handleAccountCreated, _SnapKeyring_handleAccountUpdated, _SnapKeyring_handleAccountDeleted, _SnapKeyring_handleGetSelectedAccounts, _SnapKeyring_handleRequestApproved, _SnapKeyring_handleRequestRejected, _SnapKeyring_rePublishAccountEvent, _SnapKeyring_handleAccountBalancesUpdated, _SnapKeyring_handleAccountAssetListUpdated, _SnapKeyring_handleAccountTransactionsUpdated, _SnapKeyring_getAccount, _SnapKeyring_submitRequest, _SnapKeyring_submitSnapRequest, _SnapKeyring_hasMethod, _SnapKeyring_createRequestPromise, _SnapKeyring_clearRequestPromise, _SnapKeyring_handleSyncResponse, _SnapKeyring_handleAsyncResponse, _SnapKeyring_sanitizeRedirectUrl, _SnapKeyring_validateRedirectUrl, _SnapKeyring_deleteAccount, _SnapKeyring_resolveAddress, _SnapKeyring_updateSelectedAccountsMap, _SnapKeyring_getSnap, _SnapKeyring_getSnapMetadata, _SnapKeyring_getSnapAllowedOrigins, _SnapKeyring_transformToInternalAccount;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.SnapKeyring = exports.SNAP_KEYRING_TYPE = void 0;
16
16
  const tx_1 = require("@ethereumjs/tx");
@@ -28,11 +28,11 @@ const account_1 = require("./account.cjs");
28
28
  const DeferredPromise_1 = require("./DeferredPromise.cjs");
29
29
  const events_1 = require("./events.cjs");
30
30
  const logger_1 = require("./logger.cjs");
31
+ const migrations_1 = require("./migrations/index.cjs");
31
32
  const options_1 = require("./options.cjs");
32
33
  const platform_versions_1 = require("./platform-versions.cjs");
33
34
  const SnapIdMap_1 = require("./SnapIdMap.cjs");
34
35
  const SnapKeyringMessenger_1 = require("./SnapKeyringMessenger.cjs");
35
- const SnapKeyringV2_1 = require("./SnapKeyringV2.cjs");
36
36
  const types_1 = require("./types.cjs");
37
37
  const util_1 = require("./util.cjs");
38
38
  exports.SNAP_KEYRING_TYPE = 'Snap Keyring';
@@ -77,20 +77,10 @@ class SnapKeyring {
77
77
  */
78
78
  _SnapKeyring_snapClient.set(this, void 0);
79
79
  /**
80
- * Per-snap keyring wrappers.
81
- *
82
- * Each entry owns the registry for a single Snap. Accounts are stored and
83
- * looked up through these wrappers rather than through the old flat map.
84
- */
85
- _SnapKeyring_snapKeyrings.set(this, void 0);
86
- /**
87
- * Reverse index from account ID to the owning Snap ID.
88
- *
89
- * Populated and kept in sync via the `onRegister` / `onUnregister`
90
- * callbacks injected into each `SnapKeyringV2`. Enables O(1) routing for
91
- * any "which snap owns this account?" query.
80
+ * Mapping between account IDs and an object that contains the associated
81
+ * account object and Snap ID.
92
82
  */
93
- _SnapKeyring_accountIndex.set(this, void 0);
83
+ _SnapKeyring_accounts.set(this, void 0);
94
84
  /**
95
85
  * Mapping between Snap IDs and the selected accounts.
96
86
  */
@@ -122,8 +112,7 @@ class SnapKeyring {
122
112
  __classPrivateFieldSet(this, _SnapKeyring_messenger, messenger, "f");
123
113
  __classPrivateFieldSet(this, _SnapKeyring_snapClient, new keyring_internal_snap_client_1.KeyringInternalSnapClient({ messenger }), "f");
124
114
  __classPrivateFieldSet(this, _SnapKeyring_requests, new SnapIdMap_1.SnapIdMap(), "f");
125
- __classPrivateFieldSet(this, _SnapKeyring_snapKeyrings, new Map(), "f");
126
- __classPrivateFieldSet(this, _SnapKeyring_accountIndex, new Map(), "f");
115
+ __classPrivateFieldSet(this, _SnapKeyring_accounts, new SnapIdMap_1.SnapIdMap(), "f");
127
116
  __classPrivateFieldSet(this, _SnapKeyring_options, new SnapIdMap_1.SnapIdMap(), "f");
128
117
  __classPrivateFieldSet(this, _SnapKeyring_callbacks, callbacks, "f");
129
118
  __classPrivateFieldSet(this, _SnapKeyring_isAnyAccountTypeAllowed, isAnyAccountTypeAllowed, "f");
@@ -175,27 +164,16 @@ class SnapKeyring {
175
164
  /**
176
165
  * Serialize the keyring state.
177
166
  *
178
- * Delegates to each per-snap wrapper and flattens back into the original
179
- * external format so that `KeyringController` sees no change.
180
- *
181
167
  * @returns Serialized keyring state.
182
168
  */
183
169
  async serialize() {
184
- const accounts = {};
185
- for (const wrapper of __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").values()) {
186
- for (const account of wrapper.accounts()) {
187
- accounts[account.id] = { account, snapId: wrapper.snapId };
188
- }
189
- }
190
- return { accounts };
170
+ return {
171
+ accounts: __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").toObject(),
172
+ };
191
173
  }
192
174
  /**
193
175
  * Deserialize the keyring state into this keyring.
194
176
  *
195
- * Groups the flat persisted state by `snapId`, clears both indexes, then
196
- * rebuilds each per-snap wrapper. The `onRegister` callbacks fired during
197
- * `wrapper.deserialize()` automatically repopulate `#accountIndex`.
198
- *
199
177
  * @param state - Serialized keyring state.
200
178
  */
201
179
  async deserialize(state) {
@@ -204,23 +182,26 @@ class SnapKeyring {
204
182
  if (state === undefined) {
205
183
  return;
206
184
  }
207
- // Group flat state by snapId. Migrations and migration logging are handled
208
- // inside wrapper.deserialize().
209
- const bySnap = new Map();
210
- for (const entry of Object.values(state.accounts)) {
211
- const snapAccounts = bySnap.get(entry.snapId) ?? {};
212
- snapAccounts[entry.account.id] = entry.account;
213
- bySnap.set(entry.snapId, snapAccounts);
214
- }
215
- // Clear both indexes before rebuilding — they must always be consistent.
216
- __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").clear();
217
- __classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").clear();
218
- // Rebuild per-snap wrappers. Migrations run inside wrapper.deserialize().
219
- for (const [snapId, accounts] of bySnap) {
220
- const wrapper = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getOrCreateKeyringV2).call(this, snapId);
221
- wrapper.deserialize({ snapId, accounts });
222
- // onRegister callbacks fired above have repopulated #accountIndex.
185
+ // Running Snap keyring migrations. We might have some accounts that have a
186
+ // different "version" than the one we expect.
187
+ //
188
+ // In this case, we "transform" then directly when deserializing to convert
189
+ // them in the final account version.
190
+ const accounts = {};
191
+ for (const [snapId, entry] of Object.entries(state.accounts)) {
192
+ // V1 accounts are missing the scopes.
193
+ if ((0, migrations_1.isAccountV1)(entry.account)) {
194
+ console.info(`SnapKeyring - Found a KeyringAccountV1, migrating to V2: ${entry.account.id}`);
195
+ accounts[snapId] = {
196
+ ...entry,
197
+ account: (0, migrations_1.migrateAccountV1)(entry.account),
198
+ };
199
+ }
200
+ else {
201
+ accounts[snapId] = entry;
202
+ }
223
203
  }
204
+ __classPrivateFieldSet(this, _SnapKeyring_accounts, SnapIdMap_1.SnapIdMap.fromObject(accounts), "f");
224
205
  }
225
206
  /**
226
207
  * Get the addresses of the accounts in this keyring.
@@ -228,13 +209,7 @@ class SnapKeyring {
228
209
  * @returns The addresses of the accounts in this keyring.
229
210
  */
230
211
  async getAccounts() {
231
- const addresses = [];
232
- for (const wrapper of __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").values()) {
233
- for (const account of wrapper.accounts()) {
234
- addresses.push(normalizeAccountAddress(account));
235
- }
236
- }
237
- return (0, util_1.unique)(addresses);
212
+ return (0, util_1.unique)([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].map(({ account }) => normalizeAccountAddress(account)));
238
213
  }
239
214
  /**
240
215
  * Get the addresses of the accounts associated with a given Snap.
@@ -243,7 +218,9 @@ class SnapKeyring {
243
218
  * @returns The addresses of the accounts associated with the given Snap.
244
219
  */
245
220
  async getAccountsBySnapId(snapId) {
246
- return (0, util_1.unique)((__classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId)?.accounts() ?? []).map(normalizeAccountAddress));
221
+ return (0, util_1.unique)([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()]
222
+ .filter(({ snapId: accountSnapId }) => accountSnapId === snapId)
223
+ .map(({ account }) => normalizeAccountAddress(account)));
247
224
  }
248
225
  /**
249
226
  * Create an account.
@@ -330,7 +307,7 @@ class SnapKeyring {
330
307
  account = existingAccount;
331
308
  }
332
309
  else {
333
- await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_assertAccountCanBeUsed).call(this, account);
310
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_assertAccountCanBeUsed).call(this, snapId, account);
334
311
  // Also check for transient accounts that are not yet part of the keyring
335
312
  // state.
336
313
  if (accountAddresses.has(address) || accountIds.has(account.id)) {
@@ -347,9 +324,8 @@ class SnapKeyring {
347
324
  }
348
325
  // We update the keyring state only if needed.
349
326
  if (newAccounts.length > 0) {
350
- const wrapper = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getOrCreateKeyringV2).call(this, snapId);
351
327
  for (const account of newAccounts) {
352
- wrapper.setAccount(account);
328
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(account.id, { account, snapId });
353
329
  }
354
330
  // NOTE: We assume this will never fail, thus, we don't need to rollback the
355
331
  // keyring state if anything goes wrong here.
@@ -376,8 +352,7 @@ class SnapKeyring {
376
352
  * @returns `true` if the Snap ID is known, `false` otherwise.
377
353
  */
378
354
  hasSnapId(snapId) {
379
- const wrapper = __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId);
380
- return wrapper !== undefined && wrapper.accounts().length > 0;
355
+ return __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").hasSnapId(snapId);
381
356
  }
382
357
  /**
383
358
  * Resolve the Snap account's address associated from a signing request.
@@ -632,13 +607,11 @@ class SnapKeyring {
632
607
  * @returns An internal account object for the given address.
633
608
  */
634
609
  getAccountByAddress(address) {
635
- for (const wrapper of __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").values()) {
636
- const account = wrapper.lookupByAddress(address);
637
- if (account) {
638
- return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_transformToInternalAccount).call(this, account, wrapper.snapId);
639
- }
640
- }
641
- return undefined;
610
+ const accounts = [...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()];
611
+ const account = accounts.find(({ account: { address: accountAddress } }) => (0, util_1.equalsIgnoreCase)(accountAddress, address));
612
+ return account
613
+ ? __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_transformToInternalAccount).call(this, account.account, account.snapId)
614
+ : undefined;
642
615
  }
643
616
  /**
644
617
  * List all Snap keyring accounts.
@@ -648,17 +621,11 @@ class SnapKeyring {
648
621
  * @returns An array containing all Snap keyring accounts.
649
622
  */
650
623
  listAccounts() {
651
- const accounts = [];
652
- for (const wrapper of __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").values()) {
653
- for (const account of wrapper.accounts()) {
654
- accounts.push(__classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_transformToInternalAccount).call(this, account, wrapper.snapId));
655
- }
656
- }
657
- return accounts;
624
+ return [...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].map(({ account, snapId }) => __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_transformToInternalAccount).call(this, account, snapId));
658
625
  }
659
626
  }
660
627
  exports.SnapKeyring = SnapKeyring;
661
- _SnapKeyring_messenger = new WeakMap(), _SnapKeyring_snapClient = new WeakMap(), _SnapKeyring_snapKeyrings = new WeakMap(), _SnapKeyring_accountIndex = new WeakMap(), _SnapKeyring_selectedAccounts = new WeakMap(), _SnapKeyring_requests = new WeakMap(), _SnapKeyring_options = new WeakMap(), _SnapKeyring_callbacks = new WeakMap(), _SnapKeyring_isAnyAccountTypeAllowed = new WeakMap(), _SnapKeyring_lock = new WeakMap(), _SnapKeyring_instances = new WeakSet(), _SnapKeyring_withLock =
628
+ _SnapKeyring_messenger = new WeakMap(), _SnapKeyring_snapClient = new WeakMap(), _SnapKeyring_accounts = new WeakMap(), _SnapKeyring_selectedAccounts = new WeakMap(), _SnapKeyring_requests = new WeakMap(), _SnapKeyring_options = new WeakMap(), _SnapKeyring_callbacks = new WeakMap(), _SnapKeyring_isAnyAccountTypeAllowed = new WeakMap(), _SnapKeyring_lock = new WeakMap(), _SnapKeyring_instances = new WeakSet(), _SnapKeyring_withLock =
662
629
  /**
663
630
  * Execute an operation behind a lock.
664
631
  *
@@ -667,21 +634,6 @@ _SnapKeyring_messenger = new WeakMap(), _SnapKeyring_snapClient = new WeakMap(),
667
634
  */
668
635
  async function _SnapKeyring_withLock(callback) {
669
636
  return __classPrivateFieldGet(this, _SnapKeyring_lock, "f").runExclusive(callback);
670
- }, _SnapKeyring_getOrCreateKeyringV2 = function _SnapKeyring_getOrCreateKeyringV2(snapId) {
671
- let keyring = __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId);
672
- if (!keyring) {
673
- keyring = new SnapKeyringV2_1.SnapKeyringV2({
674
- snapId,
675
- onRegister: (id) => {
676
- __classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").set(id, snapId);
677
- },
678
- onUnregister: (id) => {
679
- __classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").delete(id);
680
- },
681
- });
682
- __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").set(snapId, keyring);
683
- }
684
- return keyring;
685
637
  }, _SnapKeyring_isMinimumPlatformVersion = function _SnapKeyring_isMinimumPlatformVersion(snapId, platformVersion) {
686
638
  return __classPrivateFieldGet(this, _SnapKeyring_messenger, "f").call('SnapController:isMinimumPlatformVersion', snapId, platformVersion);
687
639
  }, _SnapKeyring_assertAccountCanBeUsed =
@@ -689,10 +641,11 @@ async function _SnapKeyring_withLock(callback) {
689
641
  * Asserts that an account can be used within the Snap keyring. (e.g. generic accounts, unique
690
642
  * addresses, etc...).
691
643
  *
644
+ * @param snapId - The account's Snap ID.
692
645
  * @param account - The account to check.
693
646
  * @throws If the account cannot be used.
694
647
  */
695
- async function _SnapKeyring_assertAccountCanBeUsed(account) {
648
+ async function _SnapKeyring_assertAccountCanBeUsed(snapId, account) {
696
649
  const address = normalizeAccountAddress(account);
697
650
  // The `AnyAccountType.Account` generic account type is allowed only during
698
651
  // development, so we check whether it's allowed before continuing.
@@ -702,8 +655,7 @@ async function _SnapKeyring_assertAccountCanBeUsed(account) {
702
655
  }
703
656
  // A Snap could try to create an account with a different address but with
704
657
  // an existing ID, so the above test only is not enough.
705
- // Account IDs are globally unique across all snaps.
706
- if (__classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").has(account.id)) {
658
+ if (__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").has(snapId, account.id)) {
707
659
  throw new Error(`Account '${account.id}' already exists`);
708
660
  }
709
661
  // The UI still uses the account address to identify accounts, so we need
@@ -714,15 +666,14 @@ async function _SnapKeyring_assertAccountCanBeUsed(account) {
714
666
  }
715
667
  }, _SnapKeyring_getExistingAccount = function _SnapKeyring_getExistingAccount(snapId, account) {
716
668
  const address = normalizeAccountAddress(account);
717
- // Account creation is idempotent, so we need to check whether the account
718
- // already exists and that the right Snap is trying to "create" it again.
669
+ // Acount creation is idempotent, so we need to check whether the account already exists
670
+ // and that the right Snap is trying to "create" it again.
719
671
  // NOTE: We are not checking account object equality here. If a Snap
720
- // re-sends this event with different account data, we will ignore it.
721
- const wrapper = __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId);
722
- const existing = wrapper?.lookupAccount(account.id);
723
- if (existing !== undefined &&
724
- normalizeAccountAddress(existing) === address) {
725
- return existing;
672
+ // re-send this event with different account data, we will ignore it.
673
+ const accountEntry = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").get(snapId, account.id);
674
+ if (accountEntry !== undefined &&
675
+ normalizeAccountAddress(accountEntry.account) === address) {
676
+ return accountEntry.account;
726
677
  }
727
678
  return undefined; // Not a known account.
728
679
  }, _SnapKeyring_getInternalOptions = function _SnapKeyring_getInternalOptions(snapId, correlationId) {
@@ -765,7 +716,7 @@ async function _SnapKeyring_handleAccountCreated(snapId, message) {
765
716
  return null;
766
717
  }
767
718
  // Make sure this new account is valid.
768
- await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_assertAccountCanBeUsed).call(this, account);
719
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_assertAccountCanBeUsed).call(this, snapId, account);
769
720
  // A deferred promise that will be resolved once the Snap keyring has saved
770
721
  // its internal state.
771
722
  // This part of the flow is run asynchronously, so we have no other way of
@@ -786,7 +737,7 @@ async function _SnapKeyring_handleAccountCreated(snapId, message) {
786
737
  //
787
738
  // e.g The account creation dialog crashed on MetaMask, this callback
788
739
  // will never be called, but the Snap still has the account.
789
- __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getOrCreateKeyringV2).call(this, snapId).setAccount(account);
740
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(account.id, { account, snapId });
790
741
  // This is the "true async part". We do not `await` for this call, mainly
791
742
  // because this callback will persist the account on the client side
792
743
  // (through the `AccountsController`).
@@ -835,8 +786,7 @@ async function _SnapKeyring_handleAccountCreated(snapId, message) {
835
786
  async function _SnapKeyring_handleAccountUpdated(snapId, message) {
836
787
  (0, superstruct_1.assert)(message, events_1.AccountUpdatedEventStruct);
837
788
  const { account: newAccountFromEvent } = message.params;
838
- const wrapper = __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId);
839
- const oldAccount = wrapper?.lookupAccount(newAccountFromEvent.id) ??
789
+ const { account: oldAccount } = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").get(snapId, newAccountFromEvent.id) ??
840
790
  (0, util_1.throwError)(`Account '${newAccountFromEvent.id}' not found`);
841
791
  // Potentially migrate the account.
842
792
  const newAccount = (0, account_1.transformAccount)(newAccountFromEvent);
@@ -856,7 +806,7 @@ async function _SnapKeyring_handleAccountUpdated(snapId, message) {
856
806
  if (!(0, util_1.equalsIgnoreCase)(oldAccount.address, newAccount.address)) {
857
807
  throw new Error(`Cannot change address of account '${newAccount.id}'`);
858
808
  }
859
- __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getOrCreateKeyringV2).call(this, snapId).setAccount(newAccount);
809
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(newAccount.id, { account: newAccount, snapId });
860
810
  await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
861
811
  return null;
862
812
  }, _SnapKeyring_handleAccountDeleted =
@@ -870,15 +820,18 @@ async function _SnapKeyring_handleAccountUpdated(snapId, message) {
870
820
  async function _SnapKeyring_handleAccountDeleted(snapId, message) {
871
821
  (0, superstruct_1.assert)(message, events_1.AccountDeletedEventStruct);
872
822
  const { id } = message.params;
873
- const account = __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId)?.lookupAccount(id);
823
+ const entry = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").get(snapId, id);
874
824
  // We can ignore the case where the account was already removed from the
875
825
  // keyring, making the deletion idempotent.
876
826
  //
877
827
  // This happens when the keyring calls the Snap to delete an account, and
878
828
  // the Snap calls the keyring back with an `AccountDeleted` event.
879
- if (account === undefined) {
829
+ if (entry === undefined) {
880
830
  return null;
881
831
  }
832
+ // At this point we know that the account exists, so we can safely
833
+ // destructure it.
834
+ const { account } = entry;
882
835
  await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").removeAccount(normalizeAccountAddress(account), snapId, async (accepted) => {
883
836
  if (accepted) {
884
837
  await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
@@ -942,7 +895,7 @@ async function _SnapKeyring_rePublishAccountEvent(snapId, event, filteredEventCa
942
895
  // prevent other Snaps from updating accounts they do not own.
943
896
  const filter = (accountMapping) => {
944
897
  return Object.entries(accountMapping).reduce((filtered, [accountId, entry]) => {
945
- if (__classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").get(accountId) === snapId) {
898
+ if (__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").has(snapId, accountId)) {
946
899
  // If the Snap owns this account, we can use it.
947
900
  filtered[accountId] = entry;
948
901
  }
@@ -1001,14 +954,11 @@ async function _SnapKeyring_handleAccountTransactionsUpdated(snapId, message) {
1001
954
  return [event];
1002
955
  });
1003
956
  }, _SnapKeyring_getAccount = function _SnapKeyring_getAccount(id) {
1004
- const snapId = __classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").get(id);
1005
- const account = snapId
1006
- ? __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId)?.lookupAccount(id)
1007
- : undefined;
1008
- if (!snapId || !account) {
957
+ const found = [...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].find((entry) => entry.account.id === id);
958
+ if (!found) {
1009
959
  throw new Error(`Unable to get account: unknown account ID: '${id}'`);
1010
960
  }
1011
- return { account, snapId };
961
+ return found;
1012
962
  }, _SnapKeyring_submitRequest =
1013
963
  /**
1014
964
  * Submit a request to a Snap from an account address.
@@ -1163,8 +1113,8 @@ async function _SnapKeyring_handleAsyncResponse(redirect, snapId) {
1163
1113
  */
1164
1114
  async function _SnapKeyring_deleteAccount(snapId, account) {
1165
1115
  // Always remove the account from the maps, even if the Snap is going to
1166
- // fail to delete it. removeAccount fires onUnregister to clean #accountIndex.
1167
- __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").get(snapId)?.removeAccount(account.id);
1116
+ // fail to delete it.
1117
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").delete(snapId, account.id);
1168
1118
  try {
1169
1119
  await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).deleteAccount(account.id);
1170
1120
  }
@@ -1175,18 +1125,12 @@ async function _SnapKeyring_deleteAccount(snapId, account) {
1175
1125
  console.error(`Account '${account.address}' may not have been removed from snap '${snapId}':`, error);
1176
1126
  }
1177
1127
  }, _SnapKeyring_resolveAddress = function _SnapKeyring_resolveAddress(address) {
1178
- for (const wrapper of __classPrivateFieldGet(this, _SnapKeyring_snapKeyrings, "f").values()) {
1179
- const account = wrapper.lookupByAddress(address);
1180
- if (account) {
1181
- return { account, snapId: wrapper.snapId };
1182
- }
1183
- }
1184
- return (0, util_1.throwError)(`Account '${address}' not found`);
1128
+ return ([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].find(({ account }) => (0, util_1.equalsIgnoreCase)(account.address, address)) ?? (0, util_1.throwError)(`Account '${address}' not found`));
1185
1129
  }, _SnapKeyring_updateSelectedAccountsMap = function _SnapKeyring_updateSelectedAccountsMap(accounts) {
1186
1130
  const selectedAccounts = __classPrivateFieldGet(this, _SnapKeyring_selectedAccounts, "f");
1187
1131
  selectedAccounts.clear();
1188
1132
  for (const account of accounts) {
1189
- const snapId = __classPrivateFieldGet(this, _SnapKeyring_accountIndex, "f").get(account);
1133
+ const snapId = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").getSnapId(account);
1190
1134
  if (!snapId) {
1191
1135
  continue;
1192
1136
  }