@metamask-previews/account-tree-controller 0.4.0-preview-d90711c → 0.4.0-preview-ea7474e2

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/AccountTreeController.cjs +30 -137
  3. package/dist/AccountTreeController.cjs.map +1 -1
  4. package/dist/AccountTreeController.d.cts +10 -38
  5. package/dist/AccountTreeController.d.cts.map +1 -1
  6. package/dist/AccountTreeController.d.mts +10 -38
  7. package/dist/AccountTreeController.d.mts.map +1 -1
  8. package/dist/AccountTreeController.mjs +29 -133
  9. package/dist/AccountTreeController.mjs.map +1 -1
  10. package/dist/AccountTreeGroup.cjs +58 -0
  11. package/dist/AccountTreeGroup.cjs.map +1 -0
  12. package/dist/AccountTreeGroup.d.cts +31 -0
  13. package/dist/AccountTreeGroup.d.cts.map +1 -0
  14. package/dist/AccountTreeGroup.d.mts +31 -0
  15. package/dist/AccountTreeGroup.d.mts.map +1 -0
  16. package/dist/AccountTreeGroup.mjs +54 -0
  17. package/dist/AccountTreeGroup.mjs.map +1 -0
  18. package/dist/AccountTreeWallet.cjs +50 -0
  19. package/dist/AccountTreeWallet.cjs.map +1 -0
  20. package/dist/AccountTreeWallet.d.cts +38 -0
  21. package/dist/AccountTreeWallet.d.cts.map +1 -0
  22. package/dist/AccountTreeWallet.d.mts +38 -0
  23. package/dist/AccountTreeWallet.d.mts.map +1 -0
  24. package/dist/AccountTreeWallet.mjs +46 -0
  25. package/dist/AccountTreeWallet.mjs.map +1 -0
  26. package/dist/index.cjs +1 -7
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +4 -2
  29. package/dist/index.d.cts.map +1 -1
  30. package/dist/index.d.mts +4 -2
  31. package/dist/index.d.mts.map +1 -1
  32. package/dist/index.mjs +1 -1
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/rules/EntropySourceWalletRule.cjs +100 -0
  35. package/dist/rules/EntropySourceWalletRule.cjs.map +1 -0
  36. package/dist/rules/EntropySourceWalletRule.d.cts +19 -0
  37. package/dist/rules/EntropySourceWalletRule.d.cts.map +1 -0
  38. package/dist/rules/EntropySourceWalletRule.d.mts +19 -0
  39. package/dist/rules/EntropySourceWalletRule.d.mts.map +1 -0
  40. package/dist/rules/EntropySourceWalletRule.mjs +95 -0
  41. package/dist/rules/EntropySourceWalletRule.mjs.map +1 -0
  42. package/dist/rules/KeyringWalletRule.cjs +99 -0
  43. package/dist/rules/KeyringWalletRule.cjs.map +1 -0
  44. package/dist/rules/KeyringWalletRule.d.cts +18 -0
  45. package/dist/rules/KeyringWalletRule.d.cts.map +1 -0
  46. package/dist/rules/KeyringWalletRule.d.mts +18 -0
  47. package/dist/rules/KeyringWalletRule.d.mts.map +1 -0
  48. package/dist/rules/KeyringWalletRule.mjs +94 -0
  49. package/dist/rules/KeyringWalletRule.mjs.map +1 -0
  50. package/dist/rules/SnapWalletRule.cjs +70 -0
  51. package/dist/rules/SnapWalletRule.cjs.map +1 -0
  52. package/dist/rules/SnapWalletRule.d.cts +10 -0
  53. package/dist/rules/SnapWalletRule.d.cts.map +1 -0
  54. package/dist/rules/SnapWalletRule.d.mts +10 -0
  55. package/dist/rules/SnapWalletRule.d.mts.map +1 -0
  56. package/dist/rules/SnapWalletRule.mjs +66 -0
  57. package/dist/rules/SnapWalletRule.mjs.map +1 -0
  58. package/dist/rules/WalletRule.cjs +13 -0
  59. package/dist/rules/WalletRule.cjs.map +1 -0
  60. package/dist/rules/WalletRule.d.cts +37 -0
  61. package/dist/rules/WalletRule.d.cts.map +1 -0
  62. package/dist/rules/WalletRule.d.mts +37 -0
  63. package/dist/rules/WalletRule.d.mts.map +1 -0
  64. package/dist/rules/WalletRule.mjs +9 -0
  65. package/dist/rules/WalletRule.mjs.map +1 -0
  66. package/dist/rules/index.cjs +21 -0
  67. package/dist/rules/index.cjs.map +1 -0
  68. package/dist/rules/index.d.cts +5 -0
  69. package/dist/rules/index.d.cts.map +1 -0
  70. package/dist/rules/index.d.mts +5 -0
  71. package/dist/rules/index.d.mts.map +1 -0
  72. package/dist/rules/index.mjs +5 -0
  73. package/dist/rules/index.mjs.map +1 -0
  74. package/dist/rules/utils.cjs +15 -0
  75. package/dist/rules/utils.cjs.map +1 -0
  76. package/dist/rules/utils.d.cts +11 -0
  77. package/dist/rules/utils.d.cts.map +1 -0
  78. package/dist/rules/utils.d.mts +11 -0
  79. package/dist/rules/utils.d.mts.map +1 -0
  80. package/dist/rules/utils.mjs +11 -0
  81. package/dist/rules/utils.mjs.map +1 -0
  82. package/package.json +3 -1
  83. package/dist/names.cjs +0 -46
  84. package/dist/names.cjs.map +0 -1
  85. package/dist/names.d.cts +0 -9
  86. package/dist/names.d.cts.map +0 -1
  87. package/dist/names.d.mts +0 -9
  88. package/dist/names.d.mts.map +0 -1
  89. package/dist/names.mjs +0 -42
  90. package/dist/names.mjs.map +0 -1
@@ -9,18 +9,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  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");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_hasKeyringType, _AccountTreeController_matchGroupByEntropySource, _AccountTreeController_matchGroupBySnapId, _AccountTreeController_matchGroupByKeyringType, _AccountTreeController_getSnapName, _AccountTreeController_getEntropySourceName, _AccountTreeController_insert, _AccountTreeController_listAccounts;
12
+ var _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
13
13
  import { BaseController } from "@metamask/base-controller";
14
- import { KeyringTypes } from "@metamask/keyring-controller";
15
- import { stripSnapPrefix } from "@metamask/snaps-utils";
16
- import { getAccountWalletNameFromKeyringType } from "./names.mjs";
14
+ import { EntropySourceWalletRule, SnapWalletRule, KeyringWalletRule } from "./rules/index.mjs";
17
15
  const controllerName = 'AccountTreeController';
18
- export var AccountWalletCategory;
19
- (function (AccountWalletCategory) {
20
- AccountWalletCategory["Entropy"] = "entropy";
21
- AccountWalletCategory["Keyring"] = "keyring";
22
- AccountWalletCategory["Snap"] = "snap";
23
- })(AccountWalletCategory || (AccountWalletCategory = {}));
24
16
  const accountTreeControllerMetadata = {
25
17
  accountTree: {
26
18
  persist: false,
@@ -39,38 +31,6 @@ export function getDefaultAccountTreeControllerState() {
39
31
  },
40
32
  };
41
33
  }
42
- // TODO: For now we use this for the 2nd-level of the tree until we implements proper multichain accounts.
43
- export const DEFAULT_ACCOUNT_GROUP_UNIQUE_ID = 'default'; // This might need to be re-evaluated based on new structure
44
- export const DEFAULT_ACCOUNT_GROUP_NAME = 'Default';
45
- /**
46
- * Convert a unique ID to a wallet ID for a given category.
47
- *
48
- * @param category - A wallet category.
49
- * @param id - A unique ID.
50
- * @returns A wallet ID.
51
- */
52
- export function toAccountWalletId(category, id) {
53
- return `${category}:${id}`;
54
- }
55
- /**
56
- * Convert a wallet ID and a unique ID, to a group ID.
57
- *
58
- * @param walletId - A wallet ID.
59
- * @param id - A unique ID.
60
- * @returns A group ID.
61
- */
62
- export function toAccountGroupId(walletId, id) {
63
- return `${walletId}:${id}`;
64
- }
65
- /**
66
- * Convert a wallet ID to the default group ID.
67
- *
68
- * @param walletId - A wallet ID.
69
- * @returns The default group ID.
70
- */
71
- export function toDefaultAccountGroupId(walletId) {
72
- return toAccountGroupId(walletId, DEFAULT_ACCOUNT_GROUP_UNIQUE_ID);
73
- }
74
34
  export class AccountTreeController extends BaseController {
75
35
  /**
76
36
  * Constructor for AccountTreeController.
@@ -92,16 +52,18 @@ export class AccountTreeController extends BaseController {
92
52
  _AccountTreeController_instances.add(this);
93
53
  _AccountTreeController_reverse.set(this, void 0);
94
54
  _AccountTreeController_rules.set(this, void 0);
55
+ _AccountTreeController_wallets.set(this, void 0);
56
+ __classPrivateFieldSet(this, _AccountTreeController_wallets, new Map(), "f");
95
57
  // Reverse map to allow fast node access from an account ID.
96
58
  __classPrivateFieldSet(this, _AccountTreeController_reverse, new Map(), "f");
97
59
  // Rules to apply to construct the wallets tree.
98
60
  __classPrivateFieldSet(this, _AccountTreeController_rules, [
99
61
  // 1. We group by entropy-source
100
- (account) => __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_matchGroupByEntropySource).call(this, account),
62
+ new EntropySourceWalletRule(this.messagingSystem),
101
63
  // 2. We group by Snap ID
102
- (account) => __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_matchGroupBySnapId).call(this, account),
64
+ new SnapWalletRule(this.messagingSystem),
103
65
  // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
104
- (account) => __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_matchGroupByKeyringType).call(this, account),
66
+ new KeyringWalletRule(this.messagingSystem),
105
67
  ], "f");
106
68
  this.messagingSystem.subscribe('AccountsController:accountAdded', (account) => {
107
69
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountAdded).call(this, account);
@@ -120,8 +82,14 @@ export class AccountTreeController extends BaseController {
120
82
  state.accountTree.wallets = wallets;
121
83
  });
122
84
  }
85
+ getWallet(id) {
86
+ return __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").get(id);
87
+ }
88
+ getWallets() {
89
+ return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
90
+ }
123
91
  }
124
- _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
92
+ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
125
93
  this.update((state) => {
126
94
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
127
95
  });
@@ -137,109 +105,37 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
137
105
  }
138
106
  });
139
107
  }
140
- }, _AccountTreeController_hasKeyringType = function _AccountTreeController_hasKeyringType(account, type) {
141
- return account.metadata.keyring.type === type;
142
- }, _AccountTreeController_matchGroupByEntropySource = function _AccountTreeController_matchGroupByEntropySource(account) {
143
- let entropySource;
144
- if (__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_hasKeyringType).call(this, account, KeyringTypes.hd)) {
145
- // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?
146
- if (!account.options.entropySource) {
147
- console.warn("! Found an HD account with no entropy source: account won't be associated to its wallet");
148
- return undefined;
149
- }
150
- entropySource = account.options.entropySource;
151
- }
152
- // TODO: For now, we're not checking if the Snap is a preinstalled one, and we probably should...
153
- if (__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_hasKeyringType).call(this, account, KeyringTypes.snap) &&
154
- account.metadata.snap?.enabled) {
155
- // Not all Snaps have an entropy-source and options are not typed yet, so we have to check manually here.
156
- if (account.options.entropySource) {
157
- // We blindly trust the `entropySource` for now, but it could be wrong since it comes from a Snap.
158
- entropySource = account.options.entropySource;
159
- }
160
- }
161
- if (!entropySource) {
162
- return undefined;
163
- }
164
- // We check if we can get the name for that entropy source, if not this means this entropy does not match
165
- // any HD keyrings, thus, is invalid (this account will be grouped by another rule).
166
- const entropySourceName = __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getEntropySourceName).call(this, entropySource);
167
- if (!entropySourceName) {
168
- console.warn('! Tried to name a wallet using an unknown entropy, this should not be possible.');
169
- return undefined;
170
- }
171
- return {
172
- category: AccountWalletCategory.Entropy,
173
- id: toAccountWalletId(AccountWalletCategory.Entropy, entropySource),
174
- name: entropySourceName,
175
- };
176
- }, _AccountTreeController_matchGroupBySnapId = function _AccountTreeController_matchGroupBySnapId(account) {
177
- if (__classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_hasKeyringType).call(this, account, KeyringTypes.snap) &&
178
- account.metadata.snap &&
179
- account.metadata.snap.enabled) {
180
- const { id } = account.metadata.snap;
181
- return {
182
- category: AccountWalletCategory.Snap,
183
- id: toAccountWalletId(AccountWalletCategory.Snap, id),
184
- name: __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_getSnapName).call(this, id),
185
- };
186
- }
187
- return undefined;
188
- }, _AccountTreeController_matchGroupByKeyringType = function _AccountTreeController_matchGroupByKeyringType(account) {
189
- const { type } = account.metadata.keyring;
190
- return {
191
- category: AccountWalletCategory.Keyring,
192
- id: toAccountWalletId(AccountWalletCategory.Keyring, type),
193
- name: getAccountWalletNameFromKeyringType(type),
194
- };
195
- }, _AccountTreeController_getSnapName = function _AccountTreeController_getSnapName(snapId) {
196
- const snap = this.messagingSystem.call('SnapController:get', snapId);
197
- const snapName = snap
198
- ? // TODO: Handle localization here, but that's a "client thing", so we don't have a `core` controller
199
- // to refer to.
200
- snap.manifest.proposedName
201
- : stripSnapPrefix(snapId);
202
- return snapName;
203
- }, _AccountTreeController_getEntropySourceName = function _AccountTreeController_getEntropySourceName(entropySource) {
204
- const { keyrings } = this.messagingSystem.call('KeyringController:getState');
205
- const index = keyrings
206
- .filter((keyring) => keyring.type === KeyringTypes.hd)
207
- .findIndex((keyring) => keyring.metadata.id === entropySource);
208
- if (index === -1) {
209
- return undefined;
210
- }
211
- return `Wallet ${index + 1}`; // Use human indexing.
212
108
  }, _AccountTreeController_insert = function _AccountTreeController_insert(wallets, account) {
213
109
  for (const rule of __classPrivateFieldGet(this, _AccountTreeController_rules, "f")) {
214
- const match = rule(account);
110
+ const match = rule.match(account);
215
111
  if (!match) {
216
112
  // No match for that rule, we go to the next one.
217
113
  continue;
218
114
  }
219
- const walletId = match.id;
220
- const walletName = match.name;
221
- const groupId = toDefaultAccountGroupId(walletId); // Use a single-group for now until multichain accounts is supported.
222
- const groupName = DEFAULT_ACCOUNT_GROUP_NAME;
223
- if (!wallets[walletId]) {
224
- wallets[walletId] = {
225
- id: walletId,
115
+ const { wallet, group } = match;
116
+ // Update in-memory wallet/group instances.
117
+ __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, wallet);
118
+ // Update controller's state.
119
+ if (!wallets[wallet.id]) {
120
+ wallets[wallet.id] = {
121
+ id: wallet.id,
226
122
  groups: {
227
- [groupId]: {
228
- id: groupId,
123
+ [group.id]: {
124
+ id: group.id,
229
125
  accounts: [],
230
- metadata: { name: groupName },
126
+ metadata: { name: group.getDefaultName() },
231
127
  },
232
128
  },
233
129
  metadata: {
234
- name: walletName,
130
+ name: wallet.getDefaultName(),
235
131
  },
236
132
  };
237
133
  }
238
- wallets[walletId].groups[groupId].accounts.push(account.id);
134
+ wallets[wallet.id].groups[group.id].accounts.push(account.id);
239
135
  // Update the reverse mapping for this account.
240
136
  __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").set(account.id, {
241
- walletId,
242
- groupId,
137
+ walletId: wallet.id,
138
+ groupId: group.id,
243
139
  });
244
140
  return;
245
141
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AccountId,\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerListMultichainAccountsAction,\n} from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport {\n type ControllerGetStateAction,\n type ControllerStateChangeEvent,\n type RestrictedMessenger,\n BaseController,\n} from '@metamask/base-controller';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport { stripSnapPrefix } from '@metamask/snaps-utils';\n\nimport { getAccountWalletNameFromKeyringType } from './names';\n\nconst controllerName = 'AccountTreeController';\n\nexport enum AccountWalletCategory {\n Entropy = 'entropy',\n Keyring = 'keyring',\n Snap = 'snap',\n}\n\ntype AccountTreeRuleMatch = {\n category: AccountWalletCategory;\n id: AccountWalletId;\n name: string;\n};\n\ntype AccountTreeRuleFunction = (\n account: InternalAccount,\n) => AccountTreeRuleMatch | undefined;\n\ntype AccountReverseMapping = {\n walletId: AccountWalletId;\n groupId: AccountGroupId;\n};\n\nexport type AccountWalletId = `${AccountWalletCategory}:${string}`;\nexport type AccountGroupId = `${AccountWalletId}:${string}`;\n\n// Do not export this one, we just use it to have a common type interface between group and wallet metadata.\ntype Metadata = {\n name: string;\n};\n\nexport type AccountWalletMetadata = Metadata;\n\nexport type AccountGroupMetadata = Metadata;\n\nexport type AccountGroup = {\n id: AccountGroupId;\n // Blockchain Accounts:\n accounts: AccountId[];\n metadata: AccountGroupMetadata;\n};\n\nexport type AccountWallet = {\n id: AccountWalletId;\n // Account groups OR Multichain accounts (once available).\n groups: {\n [groupId: AccountGroupId]: AccountGroup;\n };\n metadata: AccountWalletMetadata;\n};\n\nexport type AccountTreeControllerState = {\n accountTree: {\n wallets: {\n // Wallets:\n [walletId: AccountWalletId]: AccountWallet;\n };\n };\n};\n\nexport type AccountTreeControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedActions =\n | AccountsControllerListMultichainAccountsAction\n | KeyringControllerGetStateAction\n | SnapControllerGetSnap;\n\nexport type AccountTreeControllerActions = never;\n\nexport type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent;\n\nexport type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;\n\nexport type AccountTreeControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTreeControllerActions | AllowedActions,\n AccountTreeControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n },\n };\n}\n\n// TODO: For now we use this for the 2nd-level of the tree until we implements proper multichain accounts.\nexport const DEFAULT_ACCOUNT_GROUP_UNIQUE_ID: string = 'default'; // This might need to be re-evaluated based on new structure\nexport const DEFAULT_ACCOUNT_GROUP_NAME: string = 'Default';\n\n/**\n * Convert a unique ID to a wallet ID for a given category.\n *\n * @param category - A wallet category.\n * @param id - A unique ID.\n * @returns A wallet ID.\n */\nexport function toAccountWalletId(\n category: AccountWalletCategory,\n id: string,\n): AccountWalletId {\n return `${category}:${id}`;\n}\n\n/**\n * Convert a wallet ID and a unique ID, to a group ID.\n *\n * @param walletId - A wallet ID.\n * @param id - A unique ID.\n * @returns A group ID.\n */\nexport function toAccountGroupId(\n walletId: AccountWalletId,\n id: string,\n): AccountGroupId {\n return `${walletId}:${id}`;\n}\n\n/**\n * Convert a wallet ID to the default group ID.\n *\n * @param walletId - A wallet ID.\n * @returns The default group ID.\n */\nexport function toDefaultAccountGroupId(\n walletId: AccountWalletId,\n): AccountGroupId {\n return toAccountGroupId(walletId, DEFAULT_ACCOUNT_GROUP_UNIQUE_ID);\n}\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #reverse: Map<AccountId, AccountReverseMapping>;\n\n readonly #rules: AccountTreeRuleFunction[];\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n\n // Reverse map to allow fast node access from an account ID.\n this.#reverse = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n (account: InternalAccount) => this.#matchGroupByEntropySource(account),\n // 2. We group by Snap ID\n (account: InternalAccount) => this.#matchGroupBySnapId(account),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n (account: InternalAccount) => this.#matchGroupByKeyringType(account),\n ];\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n }\n\n init() {\n const wallets = {};\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n });\n }\n\n #handleAccountAdded(account: InternalAccount) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const found = this.#reverse.get(accountId);\n\n if (found) {\n const { walletId, groupId } = found;\n this.update((state) => {\n const { accounts } =\n state.accountTree.wallets[walletId].groups[groupId];\n\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n }\n });\n }\n }\n\n #hasKeyringType(account: InternalAccount, type: KeyringTypes): boolean {\n return account.metadata.keyring.type === (type as string);\n }\n\n #matchGroupByEntropySource(\n account: InternalAccount,\n ): AccountTreeRuleMatch | undefined {\n let entropySource: string | undefined;\n\n if (this.#hasKeyringType(account, KeyringTypes.hd)) {\n // TODO: Maybe use superstruct to validate the structure of HD account since they are not strongly-typed for now?\n if (!account.options.entropySource) {\n console.warn(\n \"! Found an HD account with no entropy source: account won't be associated to its wallet\",\n );\n return undefined;\n }\n\n entropySource = account.options.entropySource as string;\n }\n\n // TODO: For now, we're not checking if the Snap is a preinstalled one, and we probably should...\n if (\n this.#hasKeyringType(account, KeyringTypes.snap) &&\n account.metadata.snap?.enabled\n ) {\n // Not all Snaps have an entropy-source and options are not typed yet, so we have to check manually here.\n if (account.options.entropySource) {\n // We blindly trust the `entropySource` for now, but it could be wrong since it comes from a Snap.\n entropySource = account.options.entropySource as string;\n }\n }\n\n if (!entropySource) {\n return undefined;\n }\n\n // We check if we can get the name for that entropy source, if not this means this entropy does not match\n // any HD keyrings, thus, is invalid (this account will be grouped by another rule).\n const entropySourceName = this.#getEntropySourceName(entropySource);\n if (!entropySourceName) {\n console.warn(\n '! Tried to name a wallet using an unknown entropy, this should not be possible.',\n );\n return undefined;\n }\n\n return {\n category: AccountWalletCategory.Entropy,\n id: toAccountWalletId(AccountWalletCategory.Entropy, entropySource),\n name: entropySourceName,\n };\n }\n\n #matchGroupBySnapId(\n account: InternalAccount,\n ): AccountTreeRuleMatch | undefined {\n if (\n this.#hasKeyringType(account, KeyringTypes.snap) &&\n account.metadata.snap &&\n account.metadata.snap.enabled\n ) {\n const { id } = account.metadata.snap;\n\n return {\n category: AccountWalletCategory.Snap,\n id: toAccountWalletId(AccountWalletCategory.Snap, id),\n name: this.#getSnapName(id as SnapId),\n };\n }\n\n return undefined;\n }\n\n #matchGroupByKeyringType(\n account: InternalAccount,\n ): AccountTreeRuleMatch | undefined {\n const { type } = account.metadata.keyring;\n\n return {\n category: AccountWalletCategory.Keyring,\n id: toAccountWalletId(AccountWalletCategory.Keyring, type),\n name: getAccountWalletNameFromKeyringType(type as KeyringTypes),\n };\n }\n\n #getSnapName(snapId: SnapId): string {\n const snap = this.messagingSystem.call('SnapController:get', snapId);\n const snapName = snap\n ? // TODO: Handle localization here, but that's a \"client thing\", so we don't have a `core` controller\n // to refer to.\n snap.manifest.proposedName\n : stripSnapPrefix(snapId);\n\n return snapName;\n }\n\n #getEntropySourceName(entropySource: string): string | undefined {\n const { keyrings } = this.messagingSystem.call(\n 'KeyringController:getState',\n );\n\n const index = keyrings\n .filter((keyring) => keyring.type === (KeyringTypes.hd as string))\n .findIndex((keyring) => keyring.metadata.id === entropySource);\n\n if (index === -1) {\n return undefined;\n }\n\n return `Wallet ${index + 1}`; // Use human indexing.\n }\n\n #insert(\n wallets: { [walletId: AccountWalletId]: AccountWallet },\n account: InternalAccount,\n ) {\n for (const rule of this.#rules) {\n const match = rule(account);\n\n if (!match) {\n // No match for that rule, we go to the next one.\n continue;\n }\n\n const walletId = match.id;\n const walletName = match.name;\n const groupId = toDefaultAccountGroupId(walletId); // Use a single-group for now until multichain accounts is supported.\n const groupName = DEFAULT_ACCOUNT_GROUP_NAME;\n\n if (!wallets[walletId]) {\n wallets[walletId] = {\n id: walletId,\n groups: {\n [groupId]: {\n id: groupId,\n accounts: [],\n metadata: { name: groupName },\n },\n },\n metadata: {\n name: walletName,\n },\n };\n }\n wallets[walletId].groups[groupId].accounts.push(account.id);\n\n // Update the reverse mapping for this account.\n this.#reverse.set(account.id, {\n walletId,\n groupId,\n });\n\n return;\n }\n }\n\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n}\n"]}
1
+ {"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":";;;;;;;;;;;;AASA,OAAO,EAIL,cAAc,EACf,kCAAkC;AAOnC,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,iBAAiB,EAClB,0BAAgB;AAEjB,MAAM,cAAc,GAAG,uBAAuB,CAAC;AAyE/C,MAAM,6BAA6B,GACjC;IACE,WAAW,EAAE;QACX,OAAO,EAAE,KAAK;QACd,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEJ;;;;GAIG;AACH,MAAM,UAAU,oCAAoC;IAClD,OAAO;QACL,WAAW,EAAE;YACX,OAAO,EAAE,EAAE;SACZ;KACF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,qBAAsB,SAAQ,cAI1C;IAOC;;;;;;OAMG;IACH,YAAY,EACV,SAAS,EACT,KAAK,GAIN;QACC,KAAK,CAAC;YACJ,SAAS;YACT,IAAI,EAAE,cAAc;YACpB,QAAQ,EAAE,6BAA6B;YACvC,KAAK,EAAE;gBACL,GAAG,oCAAoC,EAAE;gBACzC,GAAG,KAAK;aACT;SACF,CAAC,CAAC;;QA5BI,iDAAgD;QAEhD,+CAAqB;QAErB,iDAAkD;QAyBzD,uBAAA,IAAI,kCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;QAE1B,4DAA4D;QAC5D,uBAAA,IAAI,kCAAY,IAAI,GAAG,EAAE,MAAA,CAAC;QAE1B,gDAAgD;QAChD,uBAAA,IAAI,gCAAU;YACZ,gCAAgC;YAChC,IAAI,uBAAuB,CAAC,IAAI,CAAC,eAAe,CAAC;YACjD,yBAAyB;YACzB,IAAI,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC;YACxC,8FAA8F;YAC9F,IAAI,iBAAiB,CAAC,IAAI,CAAC,eAAe,CAAC;SAC5C,MAAA,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,iCAAiC,EACjC,CAAC,OAAO,EAAE,EAAE;YACV,uBAAA,IAAI,mFAAoB,MAAxB,IAAI,EAAqB,OAAO,CAAC,CAAC;QACpC,CAAC,CACF,CAAC;QAEF,IAAI,CAAC,eAAe,CAAC,SAAS,CAC5B,mCAAmC,EACnC,CAAC,SAAS,EAAE,EAAE;YACZ,uBAAA,IAAI,qFAAsB,MAA1B,IAAI,EAAuB,SAAS,CAAC,CAAC;QACxC,CAAC,CACF,CAAC;IACJ,CAAC;IAED,IAAI;QACF,MAAM,OAAO,GAAG,EAAE,CAAC;QAEnB,kFAAkF;QAClF,KAAK,MAAM,OAAO,IAAI,uBAAA,IAAI,6EAAc,MAAlB,IAAI,CAAgB,EAAE;YAC1C,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,OAAO,EAAE,OAAO,CAAC,CAAC;SAChC;QAED,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;QACtC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,CAAC,EAAmB;QAC3B,OAAO,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,sCAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;CA2EF;+RAzEqB,OAAwB;IAC1C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,uBAAA,IAAI,uEAAQ,MAAZ,IAAI,EAAS,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,qGAEqB,SAAoB;IACxC,MAAM,KAAK,GAAG,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAE3C,IAAI,KAAK,EAAE;QACT,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,EAAE,QAAQ,EAAE,GAChB,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEtD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC1C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE;gBAChB,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;aAC3B;QACH,CAAC,CAAC,CAAC;KACJ;AACH,CAAC,yEAGC,OAA6D,EAC7D,OAAwB;IAExB,KAAK,MAAM,IAAI,IAAI,uBAAA,IAAI,oCAAO,EAAE;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,EAAE;YACV,iDAAiD;YACjD,SAAS;SACV;QAED,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;QAEhC,2CAA2C;QAC3C,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAErC,6BAA6B;QAC7B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACvB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG;gBACnB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,MAAM,EAAE;oBACN,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;wBACV,EAAE,EAAE,KAAK,CAAC,EAAE;wBACZ,QAAQ,EAAE,EAAE;wBACZ,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,cAAc,EAAE,EAAE;qBAC3C;iBACF;gBACD,QAAQ,EAAE;oBACR,IAAI,EAAE,MAAM,CAAC,cAAc,EAAE;iBAC9B;aACF,CAAC;SACH;QACD,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE9D,+CAA+C;QAC/C,uBAAA,IAAI,sCAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE;YAC5B,QAAQ,EAAE,MAAM,CAAC,EAAE;YACnB,OAAO,EAAE,KAAK,CAAC,EAAE;SAClB,CAAC,CAAC;QAEH,OAAO;KACR;AACH,CAAC;IAGC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAC9B,2CAA2C,CAC5C,CAAC;AACJ,CAAC","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport type {\n AccountId,\n AccountsControllerAccountAddedEvent,\n AccountsControllerAccountRemovedEvent,\n AccountsControllerGetAccountAction,\n AccountsControllerListMultichainAccountsAction,\n} from '@metamask/accounts-controller';\nimport type { StateMetadata } from '@metamask/base-controller';\nimport {\n type ControllerGetStateAction,\n type ControllerStateChangeEvent,\n type RestrictedMessenger,\n BaseController,\n} from '@metamask/base-controller';\nimport type { KeyringControllerGetStateAction } from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers';\n\nimport type { AccountTreeWallet } from './AccountTreeWallet';\nimport type { WalletRule } from './rules';\nimport {\n EntropySourceWalletRule,\n SnapWalletRule,\n KeyringWalletRule,\n} from './rules';\n\nconst controllerName = 'AccountTreeController';\n\ntype AccountReverseMapping = {\n walletId: AccountWalletId;\n groupId: AccountGroupId;\n};\n\n// Do not export this one, we just use it to have a common type interface between group and wallet metadata.\ntype Metadata = {\n name: string;\n};\n\nexport type AccountWalletMetadata = Metadata;\n\nexport type AccountGroupMetadata = Metadata;\n\nexport type AccountGroupObject = {\n id: AccountGroupId;\n // Blockchain Accounts:\n accounts: AccountId[];\n metadata: AccountGroupMetadata;\n};\n\nexport type AccountWalletObject = {\n id: AccountWalletId;\n // Account groups OR Multichain accounts (once available).\n groups: {\n [groupId: AccountGroupId]: AccountGroupObject;\n };\n metadata: AccountWalletMetadata;\n};\n\nexport type AccountTreeControllerState = {\n accountTree: {\n wallets: {\n // Wallets:\n [walletId: AccountWalletId]: AccountWalletObject;\n };\n };\n};\n\nexport type AccountTreeControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedActions =\n | AccountsControllerGetAccountAction\n | AccountsControllerListMultichainAccountsAction\n | KeyringControllerGetStateAction\n | SnapControllerGetSnap;\n\nexport type AccountTreeControllerActions = never;\n\nexport type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountTreeControllerState\n>;\n\nexport type AllowedEvents =\n | AccountsControllerAccountAddedEvent\n | AccountsControllerAccountRemovedEvent;\n\nexport type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;\n\nexport type AccountTreeControllerMessenger = RestrictedMessenger<\n typeof controllerName,\n AccountTreeControllerActions | AllowedActions,\n AccountTreeControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\nconst accountTreeControllerMetadata: StateMetadata<AccountTreeControllerState> =\n {\n accountTree: {\n persist: false, // We do re-recompute this state everytime.\n anonymous: false,\n },\n };\n\n/**\n * Gets default state of the `AccountTreeController`.\n *\n * @returns The default state of the `AccountTreeController`.\n */\nexport function getDefaultAccountTreeControllerState(): AccountTreeControllerState {\n return {\n accountTree: {\n wallets: {},\n },\n };\n}\n\nexport class AccountTreeController extends BaseController<\n typeof controllerName,\n AccountTreeControllerState,\n AccountTreeControllerMessenger\n> {\n readonly #reverse: Map<AccountId, AccountReverseMapping>;\n\n readonly #rules: WalletRule[];\n\n readonly #wallets: Map<AccountWalletId, AccountTreeWallet>;\n\n /**\n * Constructor for AccountTreeController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: AccountTreeControllerMessenger;\n state?: Partial<AccountTreeControllerState>;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountTreeControllerMetadata,\n state: {\n ...getDefaultAccountTreeControllerState(),\n ...state,\n },\n });\n this.#wallets = new Map();\n\n // Reverse map to allow fast node access from an account ID.\n this.#reverse = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#rules = [\n // 1. We group by entropy-source\n new EntropySourceWalletRule(this.messagingSystem),\n // 2. We group by Snap ID\n new SnapWalletRule(this.messagingSystem),\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n new KeyringWalletRule(this.messagingSystem),\n ];\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountAdded',\n (account) => {\n this.#handleAccountAdded(account);\n },\n );\n\n this.messagingSystem.subscribe(\n 'AccountsController:accountRemoved',\n (accountId) => {\n this.#handleAccountRemoved(accountId);\n },\n );\n }\n\n init() {\n const wallets = {};\n\n // For now, we always re-compute all wallets, we do not re-use the existing state.\n for (const account of this.#listAccounts()) {\n this.#insert(wallets, account);\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n });\n }\n\n getWallet(id: AccountWalletId): AccountTreeWallet | undefined {\n return this.#wallets.get(id);\n }\n\n getWallets(): AccountTreeWallet[] {\n return Array.from(this.#wallets.values());\n }\n\n #handleAccountAdded(account: InternalAccount) {\n this.update((state) => {\n this.#insert(state.accountTree.wallets, account);\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const found = this.#reverse.get(accountId);\n\n if (found) {\n const { walletId, groupId } = found;\n this.update((state) => {\n const { accounts } =\n state.accountTree.wallets[walletId].groups[groupId];\n\n const index = accounts.indexOf(accountId);\n if (index !== -1) {\n accounts.splice(index, 1);\n }\n });\n }\n }\n\n #insert(\n wallets: { [walletId: AccountWalletId]: AccountWalletObject },\n account: InternalAccount,\n ) {\n for (const rule of this.#rules) {\n const match = rule.match(account);\n\n if (!match) {\n // No match for that rule, we go to the next one.\n continue;\n }\n\n const { wallet, group } = match;\n\n // Update in-memory wallet/group instances.\n this.#wallets.set(wallet.id, wallet);\n\n // Update controller's state.\n if (!wallets[wallet.id]) {\n wallets[wallet.id] = {\n id: wallet.id,\n groups: {\n [group.id]: {\n id: group.id,\n accounts: [],\n metadata: { name: group.getDefaultName() },\n },\n },\n metadata: {\n name: wallet.getDefaultName(),\n },\n };\n }\n wallets[wallet.id].groups[group.id].accounts.push(account.id);\n\n // Update the reverse mapping for this account.\n this.#reverse.set(account.id, {\n walletId: wallet.id,\n groupId: group.id,\n });\n\n return;\n }\n }\n\n #listAccounts(): InternalAccount[] {\n return this.messagingSystem.call(\n 'AccountsController:listMultichainAccounts',\n );\n }\n}\n"]}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _MutableAccountTreeGroup_accounts;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MutableAccountTreeGroup = exports.DEFAULT_ACCOUNT_GROUP_NAME = void 0;
16
+ const account_api_1 = require("@metamask/account-api");
17
+ exports.DEFAULT_ACCOUNT_GROUP_NAME = 'Default';
18
+ // This class is meant to be used internally by every rules. It exposes mutable operations
19
+ // which should not leak outside of this package.
20
+ class MutableAccountTreeGroup {
21
+ constructor(messenger, wallet, id) {
22
+ _MutableAccountTreeGroup_accounts.set(this, void 0);
23
+ this.id = (0, account_api_1.toAccountGroupId)(wallet.id, id);
24
+ this.wallet = wallet;
25
+ this.messenger = messenger;
26
+ __classPrivateFieldSet(this, _MutableAccountTreeGroup_accounts, new Set(), "f");
27
+ }
28
+ getAccountIds() {
29
+ return Array.from(__classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f")); // FIXME: Should we force the copy here?
30
+ }
31
+ getAccounts() {
32
+ const accounts = [];
33
+ for (const id of __classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f")) {
34
+ const account = this.getAccount(id);
35
+ // FIXME: I'm really not sure we should skip those but... We could be
36
+ // "de-sync" with the AccountsController and might have some dangling
37
+ // account IDs.
38
+ if (!account) {
39
+ console.warn(`! Unable to get account: "${id}"`);
40
+ continue;
41
+ }
42
+ accounts.push(account);
43
+ }
44
+ return accounts;
45
+ }
46
+ getAccount(id) {
47
+ return this.messenger.call('AccountsController:getAccount', id);
48
+ }
49
+ addAccount(account) {
50
+ __classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f").add(account.id);
51
+ }
52
+ getDefaultName() {
53
+ return exports.DEFAULT_ACCOUNT_GROUP_NAME;
54
+ }
55
+ }
56
+ exports.MutableAccountTreeGroup = MutableAccountTreeGroup;
57
+ _MutableAccountTreeGroup_accounts = new WeakMap();
58
+ //# sourceMappingURL=AccountTreeGroup.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeGroup.cjs","sourceRoot":"","sources":["../src/AccountTreeGroup.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,uDAK+B;AAKlB,QAAA,0BAA0B,GAAW,SAAS,CAAC;AAiB5D,0FAA0F;AAC1F,iDAAiD;AACjD,MAAa,uBAAuB;IASlC,YACE,SAAyC,EACzC,MAAsC,EACtC,EAAU;QALH,oDAA0B;QAOjC,IAAI,CAAC,EAAE,GAAG,IAAA,8BAAgB,EAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,uBAAA,IAAI,qCAAa,IAAI,GAAG,EAAE,MAAA,CAAC;IAC7B,CAAC;IAED,aAAa;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,yCAAU,CAAC,CAAC,CAAC,wCAAwC;IAC7E,CAAC;IAED,WAAW;QACT,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,EAAE,IAAI,uBAAA,IAAI,yCAAU,EAAE;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAEpC,qEAAqE;YACrE,qEAAqE;YACrE,eAAe;YACf,IAAI,CAAC,OAAO,EAAE;gBACZ,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;gBACjD,SAAS;aACV;YACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SACxB;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,EAAa;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAwB;QACjC,uBAAA,IAAI,yCAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,cAAc;QACZ,OAAO,kCAA0B,CAAC;IACpC,CAAC;CACF;AAtDD,0DAsDC","sourcesContent":["import {\n toAccountGroupId,\n type AccountGroup,\n type AccountGroupId,\n type AccountWallet,\n} from '@metamask/account-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { AccountId } from '@metamask/keyring-utils';\nimport type { AccountTreeControllerMessenger } from 'src';\n\nexport const DEFAULT_ACCOUNT_GROUP_NAME: string = 'Default';\n\n/**\n * Account group coming from the {@link AccountTreeController}.\n */\nexport type AccountTreeGroup = {\n /**\n * Account IDs for that account group.\n */\n getAccountIds(): AccountId[];\n\n /**\n * Gets the default name for that account group.\n */\n getDefaultName(): string;\n} & AccountGroup<InternalAccount>;\n\n// This class is meant to be used internally by every rules. It exposes mutable operations\n// which should not leak outside of this package.\nexport class MutableAccountTreeGroup implements AccountTreeGroup {\n readonly id: AccountGroupId;\n\n readonly wallet: AccountWallet<InternalAccount>;\n\n readonly messenger: AccountTreeControllerMessenger;\n\n readonly #accounts: Set<AccountId>;\n\n constructor(\n messenger: AccountTreeControllerMessenger,\n wallet: AccountWallet<InternalAccount>,\n id: string,\n ) {\n this.id = toAccountGroupId(wallet.id, id);\n this.wallet = wallet;\n this.messenger = messenger;\n\n this.#accounts = new Set();\n }\n\n getAccountIds(): AccountId[] {\n return Array.from(this.#accounts); // FIXME: Should we force the copy here?\n }\n\n getAccounts(): InternalAccount[] {\n const accounts = [];\n\n for (const id of this.#accounts) {\n const account = this.getAccount(id);\n\n // FIXME: I'm really not sure we should skip those but... We could be\n // \"de-sync\" with the AccountsController and might have some dangling\n // account IDs.\n if (!account) {\n console.warn(`! Unable to get account: \"${id}\"`);\n continue;\n }\n accounts.push(account);\n }\n return accounts;\n }\n\n getAccount(id: AccountId): InternalAccount | undefined {\n return this.messenger.call('AccountsController:getAccount', id);\n }\n\n addAccount(account: InternalAccount) {\n this.#accounts.add(account.id);\n }\n\n getDefaultName(): string {\n return DEFAULT_ACCOUNT_GROUP_NAME;\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { type AccountGroup, type AccountGroupId, type AccountWallet } from "@metamask/account-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountId } from "@metamask/keyring-utils";
4
+ import type { AccountTreeControllerMessenger } from "src";
5
+ export declare const DEFAULT_ACCOUNT_GROUP_NAME: string;
6
+ /**
7
+ * Account group coming from the {@link AccountTreeController}.
8
+ */
9
+ export type AccountTreeGroup = {
10
+ /**
11
+ * Account IDs for that account group.
12
+ */
13
+ getAccountIds(): AccountId[];
14
+ /**
15
+ * Gets the default name for that account group.
16
+ */
17
+ getDefaultName(): string;
18
+ } & AccountGroup<InternalAccount>;
19
+ export declare class MutableAccountTreeGroup implements AccountTreeGroup {
20
+ #private;
21
+ readonly id: AccountGroupId;
22
+ readonly wallet: AccountWallet<InternalAccount>;
23
+ readonly messenger: AccountTreeControllerMessenger;
24
+ constructor(messenger: AccountTreeControllerMessenger, wallet: AccountWallet<InternalAccount>, id: string);
25
+ getAccountIds(): AccountId[];
26
+ getAccounts(): InternalAccount[];
27
+ getAccount(id: AccountId): InternalAccount | undefined;
28
+ addAccount(account: InternalAccount): void;
29
+ getDefaultName(): string;
30
+ }
31
+ //# sourceMappingURL=AccountTreeGroup.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeGroup.d.cts","sourceRoot":"","sources":["../src/AccountTreeGroup.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,8BAA8B;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,gCAAgC;AACzD,OAAO,KAAK,EAAE,8BAA8B,EAAE,YAAY;AAE1D,eAAO,MAAM,0BAA0B,EAAE,MAAkB,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE,CAAC;IAE7B;;OAEG;IACH,cAAc,IAAI,MAAM,CAAC;CAC1B,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAIlC,qBAAa,uBAAwB,YAAW,gBAAgB;;IAC9D,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAE5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAEhD,QAAQ,CAAC,SAAS,EAAE,8BAA8B,CAAC;gBAKjD,SAAS,EAAE,8BAA8B,EACzC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,EACtC,EAAE,EAAE,MAAM;IASZ,aAAa,IAAI,SAAS,EAAE;IAI5B,WAAW,IAAI,eAAe,EAAE;IAkBhC,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS;IAItD,UAAU,CAAC,OAAO,EAAE,eAAe;IAInC,cAAc,IAAI,MAAM;CAGzB"}
@@ -0,0 +1,31 @@
1
+ import { type AccountGroup, type AccountGroupId, type AccountWallet } from "@metamask/account-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import type { AccountId } from "@metamask/keyring-utils";
4
+ import type { AccountTreeControllerMessenger } from "src";
5
+ export declare const DEFAULT_ACCOUNT_GROUP_NAME: string;
6
+ /**
7
+ * Account group coming from the {@link AccountTreeController}.
8
+ */
9
+ export type AccountTreeGroup = {
10
+ /**
11
+ * Account IDs for that account group.
12
+ */
13
+ getAccountIds(): AccountId[];
14
+ /**
15
+ * Gets the default name for that account group.
16
+ */
17
+ getDefaultName(): string;
18
+ } & AccountGroup<InternalAccount>;
19
+ export declare class MutableAccountTreeGroup implements AccountTreeGroup {
20
+ #private;
21
+ readonly id: AccountGroupId;
22
+ readonly wallet: AccountWallet<InternalAccount>;
23
+ readonly messenger: AccountTreeControllerMessenger;
24
+ constructor(messenger: AccountTreeControllerMessenger, wallet: AccountWallet<InternalAccount>, id: string);
25
+ getAccountIds(): AccountId[];
26
+ getAccounts(): InternalAccount[];
27
+ getAccount(id: AccountId): InternalAccount | undefined;
28
+ addAccount(account: InternalAccount): void;
29
+ getDefaultName(): string;
30
+ }
31
+ //# sourceMappingURL=AccountTreeGroup.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeGroup.d.mts","sourceRoot":"","sources":["../src/AccountTreeGroup.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,8BAA8B;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,gCAAgC;AACzD,OAAO,KAAK,EAAE,8BAA8B,EAAE,YAAY;AAE1D,eAAO,MAAM,0BAA0B,EAAE,MAAkB,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE,CAAC;IAE7B;;OAEG;IACH,cAAc,IAAI,MAAM,CAAC;CAC1B,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAIlC,qBAAa,uBAAwB,YAAW,gBAAgB;;IAC9D,QAAQ,CAAC,EAAE,EAAE,cAAc,CAAC;IAE5B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAEhD,QAAQ,CAAC,SAAS,EAAE,8BAA8B,CAAC;gBAKjD,SAAS,EAAE,8BAA8B,EACzC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,EACtC,EAAE,EAAE,MAAM;IASZ,aAAa,IAAI,SAAS,EAAE;IAI5B,WAAW,IAAI,eAAe,EAAE;IAkBhC,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,eAAe,GAAG,SAAS;IAItD,UAAU,CAAC,OAAO,EAAE,eAAe;IAInC,cAAc,IAAI,MAAM;CAGzB"}
@@ -0,0 +1,54 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ 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");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ 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");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _MutableAccountTreeGroup_accounts;
13
+ import { toAccountGroupId } from "@metamask/account-api";
14
+ export const DEFAULT_ACCOUNT_GROUP_NAME = 'Default';
15
+ // This class is meant to be used internally by every rules. It exposes mutable operations
16
+ // which should not leak outside of this package.
17
+ export class MutableAccountTreeGroup {
18
+ constructor(messenger, wallet, id) {
19
+ _MutableAccountTreeGroup_accounts.set(this, void 0);
20
+ this.id = toAccountGroupId(wallet.id, id);
21
+ this.wallet = wallet;
22
+ this.messenger = messenger;
23
+ __classPrivateFieldSet(this, _MutableAccountTreeGroup_accounts, new Set(), "f");
24
+ }
25
+ getAccountIds() {
26
+ return Array.from(__classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f")); // FIXME: Should we force the copy here?
27
+ }
28
+ getAccounts() {
29
+ const accounts = [];
30
+ for (const id of __classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f")) {
31
+ const account = this.getAccount(id);
32
+ // FIXME: I'm really not sure we should skip those but... We could be
33
+ // "de-sync" with the AccountsController and might have some dangling
34
+ // account IDs.
35
+ if (!account) {
36
+ console.warn(`! Unable to get account: "${id}"`);
37
+ continue;
38
+ }
39
+ accounts.push(account);
40
+ }
41
+ return accounts;
42
+ }
43
+ getAccount(id) {
44
+ return this.messenger.call('AccountsController:getAccount', id);
45
+ }
46
+ addAccount(account) {
47
+ __classPrivateFieldGet(this, _MutableAccountTreeGroup_accounts, "f").add(account.id);
48
+ }
49
+ getDefaultName() {
50
+ return DEFAULT_ACCOUNT_GROUP_NAME;
51
+ }
52
+ }
53
+ _MutableAccountTreeGroup_accounts = new WeakMap();
54
+ //# sourceMappingURL=AccountTreeGroup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeGroup.mjs","sourceRoot":"","sources":["../src/AccountTreeGroup.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EACL,gBAAgB,EAIjB,8BAA8B;AAK/B,MAAM,CAAC,MAAM,0BAA0B,GAAW,SAAS,CAAC;AAiB5D,0FAA0F;AAC1F,iDAAiD;AACjD,MAAM,OAAO,uBAAuB;IASlC,YACE,SAAyC,EACzC,MAAsC,EACtC,EAAU;QALH,oDAA0B;QAOjC,IAAI,CAAC,EAAE,GAAG,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,uBAAA,IAAI,qCAAa,IAAI,GAAG,EAAE,MAAA,CAAC;IAC7B,CAAC;IAED,aAAa;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,yCAAU,CAAC,CAAC,CAAC,wCAAwC;IAC7E,CAAC;IAED,WAAW;QACT,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,MAAM,EAAE,IAAI,uBAAA,IAAI,yCAAU,EAAE;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAEpC,qEAAqE;YACrE,qEAAqE;YACrE,eAAe;YACf,IAAI,CAAC,OAAO,EAAE;gBACZ,OAAO,CAAC,IAAI,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;gBACjD,SAAS;aACV;YACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SACxB;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,UAAU,CAAC,EAAa;QACtB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAwB;QACjC,uBAAA,IAAI,yCAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED,cAAc;QACZ,OAAO,0BAA0B,CAAC;IACpC,CAAC;CACF","sourcesContent":["import {\n toAccountGroupId,\n type AccountGroup,\n type AccountGroupId,\n type AccountWallet,\n} from '@metamask/account-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { AccountId } from '@metamask/keyring-utils';\nimport type { AccountTreeControllerMessenger } from 'src';\n\nexport const DEFAULT_ACCOUNT_GROUP_NAME: string = 'Default';\n\n/**\n * Account group coming from the {@link AccountTreeController}.\n */\nexport type AccountTreeGroup = {\n /**\n * Account IDs for that account group.\n */\n getAccountIds(): AccountId[];\n\n /**\n * Gets the default name for that account group.\n */\n getDefaultName(): string;\n} & AccountGroup<InternalAccount>;\n\n// This class is meant to be used internally by every rules. It exposes mutable operations\n// which should not leak outside of this package.\nexport class MutableAccountTreeGroup implements AccountTreeGroup {\n readonly id: AccountGroupId;\n\n readonly wallet: AccountWallet<InternalAccount>;\n\n readonly messenger: AccountTreeControllerMessenger;\n\n readonly #accounts: Set<AccountId>;\n\n constructor(\n messenger: AccountTreeControllerMessenger,\n wallet: AccountWallet<InternalAccount>,\n id: string,\n ) {\n this.id = toAccountGroupId(wallet.id, id);\n this.wallet = wallet;\n this.messenger = messenger;\n\n this.#accounts = new Set();\n }\n\n getAccountIds(): AccountId[] {\n return Array.from(this.#accounts); // FIXME: Should we force the copy here?\n }\n\n getAccounts(): InternalAccount[] {\n const accounts = [];\n\n for (const id of this.#accounts) {\n const account = this.getAccount(id);\n\n // FIXME: I'm really not sure we should skip those but... We could be\n // \"de-sync\" with the AccountsController and might have some dangling\n // account IDs.\n if (!account) {\n console.warn(`! Unable to get account: \"${id}\"`);\n continue;\n }\n accounts.push(account);\n }\n return accounts;\n }\n\n getAccount(id: AccountId): InternalAccount | undefined {\n return this.messenger.call('AccountsController:getAccount', id);\n }\n\n addAccount(account: InternalAccount) {\n this.#accounts.add(account.id);\n }\n\n getDefaultName(): string {\n return DEFAULT_ACCOUNT_GROUP_NAME;\n }\n}\n"]}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _MutableAccountTreeWallet_groups;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.MutableAccountTreeWallet = void 0;
16
+ const account_api_1 = require("@metamask/account-api");
17
+ const AccountTreeGroup_1 = require("./AccountTreeGroup.cjs");
18
+ // This class is meant to be used internally by every rules. It exposes mutable operations
19
+ // which should not leak outside of this package.
20
+ class MutableAccountTreeWallet {
21
+ constructor(messenger, category, id) {
22
+ _MutableAccountTreeWallet_groups.set(this, void 0);
23
+ this.id = (0, account_api_1.toAccountWalletId)(category, id);
24
+ this.category = category;
25
+ this.messenger = messenger;
26
+ __classPrivateFieldSet(this, _MutableAccountTreeWallet_groups, new Map(), "f");
27
+ }
28
+ getAccountGroup(groupId) {
29
+ return __classPrivateFieldGet(this, _MutableAccountTreeWallet_groups, "f").get(groupId);
30
+ }
31
+ getAccountGroups() {
32
+ return Array.from(__classPrivateFieldGet(this, _MutableAccountTreeWallet_groups, "f").values()); // TODO: Should we avoid the copy here?
33
+ }
34
+ // NOTE: This method SHOULD BE overriden if a rule need to group things differently.
35
+ addAccount(account) {
36
+ const id = account_api_1.DEFAULT_ACCOUNT_GROUP_UNIQUE_ID;
37
+ // Use a single-group by default.
38
+ let group = __classPrivateFieldGet(this, _MutableAccountTreeWallet_groups, "f").get((0, account_api_1.toAccountGroupId)(this.id, id));
39
+ if (!group) {
40
+ // We create the account group and attach it to this wallet.
41
+ group = new AccountTreeGroup_1.MutableAccountTreeGroup(this.messenger, this, id);
42
+ __classPrivateFieldGet(this, _MutableAccountTreeWallet_groups, "f").set(group.id, group);
43
+ }
44
+ group.addAccount(account);
45
+ return group;
46
+ }
47
+ }
48
+ exports.MutableAccountTreeWallet = MutableAccountTreeWallet;
49
+ _MutableAccountTreeWallet_groups = new WeakMap();
50
+ //# sourceMappingURL=AccountTreeWallet.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeWallet.cjs","sourceRoot":"","sources":["../src/AccountTreeWallet.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,uDAQ+B;AAK/B,6DAA6D;AA0B7D,0FAA0F;AAC1F,iDAAiD;AACjD,MAAsB,wBAAwB;IAS5C,YACE,SAAyC,EACzC,QAA+B,EAC/B,EAAU;QALH,mDAAsD;QAO7D,IAAI,CAAC,EAAE,GAAG,IAAA,+BAAiB,EAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAE3B,uBAAA,IAAI,oCAAW,IAAI,GAAG,EAAE,MAAA,CAAC;IAC3B,CAAC;IAED,eAAe,CAAC,OAAuB;QACrC,OAAO,uBAAA,IAAI,wCAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB;QACd,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,wCAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,uCAAuC;IACnF,CAAC;IAED,oFAAoF;IACpF,UAAU,CAAC,OAAwB;QACjC,MAAM,EAAE,GAAG,6CAA+B,CAAC;QAE3C,iCAAiC;QACjC,IAAI,KAAK,GAAG,uBAAA,IAAI,wCAAQ,CAAC,GAAG,CAAC,IAAA,8BAAgB,EAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,EAAE;YACV,4DAA4D;YAC5D,KAAK,GAAG,IAAI,0CAAuB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;YAC9D,uBAAA,IAAI,wCAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;SACnC;QAED,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAE1B,OAAO,KAAK,CAAC;IACf,CAAC;CAGF;AA/CD,4DA+CC","sourcesContent":["import {\n toAccountGroupId,\n toAccountWalletId,\n DEFAULT_ACCOUNT_GROUP_UNIQUE_ID,\n type AccountGroupId,\n type AccountWallet,\n type AccountWalletCategory,\n type AccountWalletId,\n} from '@metamask/account-api';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\n\nimport { type AccountTreeControllerMessenger } from './AccountTreeController';\nimport type { AccountTreeGroup } from './AccountTreeGroup';\nimport { MutableAccountTreeGroup } from './AccountTreeGroup';\n\n/**\n * Account wallet coming from the {@link AccountTreeController}.\n */\nexport type AccountTreeWallet = {\n /**\n * Gets account tree group for a given ID.\n *\n * @returns Account tree group.\n */\n getAccountGroup(groupId: AccountGroupId): AccountTreeGroup | undefined;\n\n /**\n * Gets all account tree groups.\n *\n * @returns Account tree groups.\n */\n getAccountGroups(): AccountTreeGroup[];\n\n /**\n * Gets the default name for that account wallet.\n */\n getDefaultName(): string;\n} & AccountWallet<InternalAccount>;\n\n// This class is meant to be used internally by every rules. It exposes mutable operations\n// which should not leak outside of this package.\nexport abstract class MutableAccountTreeWallet implements AccountTreeWallet {\n readonly id: AccountWalletId;\n\n readonly category: AccountWalletCategory;\n\n readonly messenger: AccountTreeControllerMessenger;\n\n readonly #groups: Map<AccountGroupId, MutableAccountTreeGroup>;\n\n constructor(\n messenger: AccountTreeControllerMessenger,\n category: AccountWalletCategory,\n id: string,\n ) {\n this.id = toAccountWalletId(category, id);\n this.category = category;\n this.messenger = messenger;\n\n this.#groups = new Map();\n }\n\n getAccountGroup(groupId: AccountGroupId): AccountTreeGroup | undefined {\n return this.#groups.get(groupId);\n }\n\n getAccountGroups(): AccountTreeGroup[] {\n return Array.from(this.#groups.values()); // TODO: Should we avoid the copy here?\n }\n\n // NOTE: This method SHOULD BE overriden if a rule need to group things differently.\n addAccount(account: InternalAccount): MutableAccountTreeGroup {\n const id = DEFAULT_ACCOUNT_GROUP_UNIQUE_ID;\n\n // Use a single-group by default.\n let group = this.#groups.get(toAccountGroupId(this.id, id));\n if (!group) {\n // We create the account group and attach it to this wallet.\n group = new MutableAccountTreeGroup(this.messenger, this, id);\n this.#groups.set(group.id, group);\n }\n\n group.addAccount(account);\n\n return group;\n }\n\n abstract getDefaultName(): string;\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { type AccountGroupId, type AccountWallet, type AccountWalletCategory, type AccountWalletId } from "@metamask/account-api";
2
+ import type { InternalAccount } from "@metamask/keyring-internal-api";
3
+ import { type AccountTreeControllerMessenger } from "./AccountTreeController.cjs";
4
+ import type { AccountTreeGroup } from "./AccountTreeGroup.cjs";
5
+ import { MutableAccountTreeGroup } from "./AccountTreeGroup.cjs";
6
+ /**
7
+ * Account wallet coming from the {@link AccountTreeController}.
8
+ */
9
+ export type AccountTreeWallet = {
10
+ /**
11
+ * Gets account tree group for a given ID.
12
+ *
13
+ * @returns Account tree group.
14
+ */
15
+ getAccountGroup(groupId: AccountGroupId): AccountTreeGroup | undefined;
16
+ /**
17
+ * Gets all account tree groups.
18
+ *
19
+ * @returns Account tree groups.
20
+ */
21
+ getAccountGroups(): AccountTreeGroup[];
22
+ /**
23
+ * Gets the default name for that account wallet.
24
+ */
25
+ getDefaultName(): string;
26
+ } & AccountWallet<InternalAccount>;
27
+ export declare abstract class MutableAccountTreeWallet implements AccountTreeWallet {
28
+ #private;
29
+ readonly id: AccountWalletId;
30
+ readonly category: AccountWalletCategory;
31
+ readonly messenger: AccountTreeControllerMessenger;
32
+ constructor(messenger: AccountTreeControllerMessenger, category: AccountWalletCategory, id: string);
33
+ getAccountGroup(groupId: AccountGroupId): AccountTreeGroup | undefined;
34
+ getAccountGroups(): AccountTreeGroup[];
35
+ addAccount(account: InternalAccount): MutableAccountTreeGroup;
36
+ abstract getDefaultName(): string;
37
+ }
38
+ //# sourceMappingURL=AccountTreeWallet.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccountTreeWallet.d.cts","sourceRoot":"","sources":["../src/AccountTreeWallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,8BAA8B;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,uCAAuC;AAEtE,OAAO,EAAE,KAAK,8BAA8B,EAAE,oCAAgC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,+BAA2B;AAC3D,OAAO,EAAE,uBAAuB,EAAE,+BAA2B;AAE7D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;IAEvE;;;;OAIG;IACH,gBAAgB,IAAI,gBAAgB,EAAE,CAAC;IAEvC;;OAEG;IACH,cAAc,IAAI,MAAM,CAAC;CAC1B,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;AAInC,8BAAsB,wBAAyB,YAAW,iBAAiB;;IACzE,QAAQ,CAAC,EAAE,EAAE,eAAe,CAAC;IAE7B,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IAEzC,QAAQ,CAAC,SAAS,EAAE,8BAA8B,CAAC;gBAKjD,SAAS,EAAE,8BAA8B,EACzC,QAAQ,EAAE,qBAAqB,EAC/B,EAAE,EAAE,MAAM;IASZ,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,gBAAgB,GAAG,SAAS;IAItE,gBAAgB,IAAI,gBAAgB,EAAE;IAKtC,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,uBAAuB;IAgB7D,QAAQ,CAAC,cAAc,IAAI,MAAM;CAClC"}