@metamask-previews/account-tree-controller 0.6.0-preview-e0bc4b4b → 0.6.0-preview-9b5151c

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 (114) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/AccountTreeController.cjs +86 -25
  3. package/dist/AccountTreeController.cjs.map +1 -1
  4. package/dist/AccountTreeController.d.cts +5 -3
  5. package/dist/AccountTreeController.d.cts.map +1 -1
  6. package/dist/AccountTreeController.d.mts +5 -3
  7. package/dist/AccountTreeController.d.mts.map +1 -1
  8. package/dist/AccountTreeController.mjs +87 -26
  9. package/dist/AccountTreeController.mjs.map +1 -1
  10. package/dist/AccountTreeGroup.cjs +51 -35
  11. package/dist/AccountTreeGroup.cjs.map +1 -1
  12. package/dist/AccountTreeGroup.d.cts +15 -22
  13. package/dist/AccountTreeGroup.d.cts.map +1 -1
  14. package/dist/AccountTreeGroup.d.mts +15 -22
  15. package/dist/AccountTreeGroup.d.mts.map +1 -1
  16. package/dist/AccountTreeGroup.mjs +49 -33
  17. package/dist/AccountTreeGroup.mjs.map +1 -1
  18. package/dist/AccountTreeWallet.cjs +61 -27
  19. package/dist/AccountTreeWallet.cjs.map +1 -1
  20. package/dist/AccountTreeWallet.d.cts +24 -17
  21. package/dist/AccountTreeWallet.d.cts.map +1 -1
  22. package/dist/AccountTreeWallet.d.mts +24 -17
  23. package/dist/AccountTreeWallet.d.mts.map +1 -1
  24. package/dist/AccountTreeWallet.mjs +60 -26
  25. package/dist/AccountTreeWallet.mjs.map +1 -1
  26. package/dist/rules/entropy.cjs +83 -0
  27. package/dist/rules/entropy.cjs.map +1 -0
  28. package/dist/rules/entropy.d.cts +15 -0
  29. package/dist/rules/entropy.d.cts.map +1 -0
  30. package/dist/rules/entropy.d.mts +15 -0
  31. package/dist/rules/entropy.d.mts.map +1 -0
  32. package/dist/rules/entropy.mjs +79 -0
  33. package/dist/rules/entropy.mjs.map +1 -0
  34. package/dist/rules/index.cjs +4 -4
  35. package/dist/rules/index.cjs.map +1 -1
  36. package/dist/rules/index.d.cts +4 -4
  37. package/dist/rules/index.d.cts.map +1 -1
  38. package/dist/rules/index.d.mts +4 -4
  39. package/dist/rules/index.d.mts.map +1 -1
  40. package/dist/rules/index.mjs +4 -4
  41. package/dist/rules/index.mjs.map +1 -1
  42. package/dist/rules/keyring.cjs +80 -0
  43. package/dist/rules/keyring.cjs.map +1 -0
  44. package/dist/rules/keyring.d.cts +20 -0
  45. package/dist/rules/keyring.d.cts.map +1 -0
  46. package/dist/rules/keyring.d.mts +20 -0
  47. package/dist/rules/keyring.d.mts.map +1 -0
  48. package/dist/rules/keyring.mjs +75 -0
  49. package/dist/rules/keyring.mjs.map +1 -0
  50. package/dist/rules/rule.cjs +10 -0
  51. package/dist/rules/rule.cjs.map +1 -0
  52. package/dist/rules/rule.d.cts +42 -0
  53. package/dist/rules/rule.d.cts.map +1 -0
  54. package/dist/rules/rule.d.mts +42 -0
  55. package/dist/rules/rule.d.mts.map +1 -0
  56. package/dist/rules/rule.mjs +6 -0
  57. package/dist/rules/rule.mjs.map +1 -0
  58. package/dist/rules/snap.cjs +53 -0
  59. package/dist/rules/snap.cjs.map +1 -0
  60. package/dist/rules/snap.d.cts +23 -0
  61. package/dist/rules/snap.d.cts.map +1 -0
  62. package/dist/rules/snap.d.mts +23 -0
  63. package/dist/rules/snap.d.mts.map +1 -0
  64. package/dist/rules/snap.mjs +49 -0
  65. package/dist/rules/snap.mjs.map +1 -0
  66. package/dist/types.cjs +3 -0
  67. package/dist/types.cjs.map +1 -0
  68. package/dist/types.d.cts +15 -0
  69. package/dist/types.d.cts.map +1 -0
  70. package/dist/types.d.mts +15 -0
  71. package/dist/types.d.mts.map +1 -0
  72. package/dist/types.mjs +2 -0
  73. package/dist/types.mjs.map +1 -0
  74. package/package.json +3 -3
  75. package/dist/rules/EntropySourceWalletRule.cjs +0 -100
  76. package/dist/rules/EntropySourceWalletRule.cjs.map +0 -1
  77. package/dist/rules/EntropySourceWalletRule.d.cts +0 -19
  78. package/dist/rules/EntropySourceWalletRule.d.cts.map +0 -1
  79. package/dist/rules/EntropySourceWalletRule.d.mts +0 -19
  80. package/dist/rules/EntropySourceWalletRule.d.mts.map +0 -1
  81. package/dist/rules/EntropySourceWalletRule.mjs +0 -95
  82. package/dist/rules/EntropySourceWalletRule.mjs.map +0 -1
  83. package/dist/rules/KeyringWalletRule.cjs +0 -99
  84. package/dist/rules/KeyringWalletRule.cjs.map +0 -1
  85. package/dist/rules/KeyringWalletRule.d.cts +0 -18
  86. package/dist/rules/KeyringWalletRule.d.cts.map +0 -1
  87. package/dist/rules/KeyringWalletRule.d.mts +0 -18
  88. package/dist/rules/KeyringWalletRule.d.mts.map +0 -1
  89. package/dist/rules/KeyringWalletRule.mjs +0 -94
  90. package/dist/rules/KeyringWalletRule.mjs.map +0 -1
  91. package/dist/rules/SnapWalletRule.cjs +0 -70
  92. package/dist/rules/SnapWalletRule.cjs.map +0 -1
  93. package/dist/rules/SnapWalletRule.d.cts +0 -10
  94. package/dist/rules/SnapWalletRule.d.cts.map +0 -1
  95. package/dist/rules/SnapWalletRule.d.mts +0 -10
  96. package/dist/rules/SnapWalletRule.d.mts.map +0 -1
  97. package/dist/rules/SnapWalletRule.mjs +0 -66
  98. package/dist/rules/SnapWalletRule.mjs.map +0 -1
  99. package/dist/rules/WalletRule.cjs +0 -13
  100. package/dist/rules/WalletRule.cjs.map +0 -1
  101. package/dist/rules/WalletRule.d.cts +0 -37
  102. package/dist/rules/WalletRule.d.cts.map +0 -1
  103. package/dist/rules/WalletRule.d.mts +0 -37
  104. package/dist/rules/WalletRule.d.mts.map +0 -1
  105. package/dist/rules/WalletRule.mjs +0 -9
  106. package/dist/rules/WalletRule.mjs.map +0 -1
  107. package/dist/rules/utils.cjs +0 -15
  108. package/dist/rules/utils.cjs.map +0 -1
  109. package/dist/rules/utils.d.cts +0 -11
  110. package/dist/rules/utils.d.cts.map +0 -1
  111. package/dist/rules/utils.d.mts +0 -11
  112. package/dist/rules/utils.d.mts.map +0 -1
  113. package/dist/rules/utils.mjs +0 -11
  114. package/dist/rules/utils.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ 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 BIP-44/multichain accounts support ([#6185](https://github.com/MetaMask/core/pull/6185))
13
+ - Those are being attached to the `entropy` wallet category.
14
+
15
+ ### Changed
16
+
17
+ - Now use one account group per account for `snap` and `keyring` wallet categories ([#6185](https://github.com/MetaMask/core/pull/6185))
18
+ - We used to group all accounts under the `'default'` group, but we now compute the group ID using the address of each accounts.
19
+ - Compute account group name based on their underlying account. ([#6185](https://github.com/MetaMask/core/pull/6185))
20
+ - This replaces the previous `'Default'` name for groups.
21
+
10
22
  ## [0.6.0]
11
23
 
12
24
  ### Changed
@@ -10,10 +10,12 @@ 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 _AccountTreeController_instances, _AccountTreeController_reverse, _AccountTreeController_rules, _AccountTreeController_wallets, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
13
+ var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_rules, _AccountTreeController_categoryToRule, _AccountTreeController_wallets, _AccountTreeController_renameAccountWallet, _AccountTreeController_renameAccountGroup, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.AccountTreeController = exports.getDefaultAccountTreeControllerState = void 0;
16
+ const account_api_1 = require("@metamask/account-api");
16
17
  const base_controller_1 = require("@metamask/base-controller");
18
+ const AccountTreeWallet_1 = require("./AccountTreeWallet.cjs");
17
19
  const rules_1 = require("./rules/index.cjs");
18
20
  const controllerName = 'AccountTreeController';
19
21
  const accountTreeControllerMetadata = {
@@ -54,20 +56,26 @@ class AccountTreeController extends base_controller_1.BaseController {
54
56
  },
55
57
  });
56
58
  _AccountTreeController_instances.add(this);
57
- _AccountTreeController_reverse.set(this, void 0);
59
+ _AccountTreeController_accountIdToContext.set(this, void 0);
58
60
  _AccountTreeController_rules.set(this, void 0);
61
+ _AccountTreeController_categoryToRule.set(this, void 0);
59
62
  _AccountTreeController_wallets.set(this, void 0);
60
63
  __classPrivateFieldSet(this, _AccountTreeController_wallets, new Map(), "f");
61
64
  // Reverse map to allow fast node access from an account ID.
62
- __classPrivateFieldSet(this, _AccountTreeController_reverse, new Map(), "f");
65
+ __classPrivateFieldSet(this, _AccountTreeController_accountIdToContext, new Map(), "f");
63
66
  // Rules to apply to construct the wallets tree.
67
+ __classPrivateFieldSet(this, _AccountTreeController_categoryToRule, {
68
+ [account_api_1.AccountWalletCategory.Entropy]: new rules_1.EntropyRule(this.messagingSystem),
69
+ [account_api_1.AccountWalletCategory.Snap]: new rules_1.SnapRule(this.messagingSystem),
70
+ [account_api_1.AccountWalletCategory.Keyring]: new rules_1.KeyringRule(this.messagingSystem),
71
+ }, "f");
64
72
  __classPrivateFieldSet(this, _AccountTreeController_rules, [
65
73
  // 1. We group by entropy-source
66
- new rules_1.EntropySourceWalletRule(this.messagingSystem),
74
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Entropy],
67
75
  // 2. We group by Snap ID
68
- new rules_1.SnapWalletRule(this.messagingSystem),
76
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Snap],
69
77
  // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
70
- new rules_1.KeyringWalletRule(this.messagingSystem),
78
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[account_api_1.AccountWalletCategory.Keyring],
71
79
  ], "f");
72
80
  this.messagingSystem.subscribe('AccountsController:accountAdded', (account) => {
73
81
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountAdded).call(this, account);
@@ -82,6 +90,23 @@ class AccountTreeController extends base_controller_1.BaseController {
82
90
  for (const account of __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this)) {
83
91
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
84
92
  }
93
+ // Once we have the account tree, we can compute the name.
94
+ for (const wallet of Object.values(wallets)) {
95
+ const walletInstance = this.getWallet(wallet.id);
96
+ if (walletInstance) {
97
+ if (wallet.metadata.name === '') {
98
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWallet).call(this, walletInstance, wallet);
99
+ }
100
+ for (const group of Object.values(wallet.groups)) {
101
+ const groupInstance = walletInstance.getAccountGroup(group.id);
102
+ if (groupInstance) {
103
+ if (group.metadata.name === '') {
104
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroup).call(this, groupInstance, group);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
85
110
  this.update((state) => {
86
111
  state.accountTree.wallets = wallets;
87
112
  });
@@ -94,14 +119,42 @@ class AccountTreeController extends base_controller_1.BaseController {
94
119
  }
95
120
  }
96
121
  exports.AccountTreeController = AccountTreeController;
97
- _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
122
+ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_categoryToRule = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_renameAccountWallet = function _AccountTreeController_renameAccountWallet(wallet, walletObject) {
123
+ const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[walletObject.category];
124
+ walletObject.metadata.name = rule.getDefaultAccountWalletName(wallet);
125
+ }, _AccountTreeController_renameAccountGroup = function _AccountTreeController_renameAccountGroup(group, groupObject) {
126
+ const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[group.wallet.category];
127
+ groupObject.metadata.name = rule.getDefaultAccountGroupName(group);
128
+ }, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
98
129
  this.update((state) => {
99
130
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
131
+ const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
132
+ if (context) {
133
+ const { walletId, groupId } = context;
134
+ const wallet = state.accountTree.wallets[walletId];
135
+ if (wallet) {
136
+ const walletInstance = this.getWallet(wallet.id);
137
+ if (walletInstance) {
138
+ if (wallet.metadata.name === '') {
139
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWallet).call(this, walletInstance, wallet);
140
+ }
141
+ const group = wallet.groups[groupId];
142
+ if (group) {
143
+ const groupInstance = walletInstance.getAccountGroup(group.id);
144
+ if (groupInstance) {
145
+ if (group.metadata.name === '') {
146
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroup).call(this, groupInstance, group);
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
100
153
  });
101
154
  }, _AccountTreeController_handleAccountRemoved = function _AccountTreeController_handleAccountRemoved(accountId) {
102
- const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
103
- if (found) {
104
- const { walletId, groupId } = found;
155
+ const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(accountId);
156
+ if (context) {
157
+ const { walletId, groupId } = context;
105
158
  this.update((state) => {
106
159
  const { accounts } = state.accountTree.wallets[walletId].groups[groupId];
107
160
  const index = accounts.indexOf(accountId);
@@ -117,28 +170,36 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
117
170
  // No match for that rule, we go to the next one.
118
171
  continue;
119
172
  }
120
- const { wallet, group } = match;
121
- // Update in-memory wallet/group instances.
122
- __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, wallet);
173
+ const { walletId, groupId } = match;
123
174
  // Update controller's state.
124
- if (!wallets[wallet.id]) {
125
- wallets[wallet.id] = {
126
- id: wallet.id,
127
- groups: {
128
- [group.id]: {
129
- id: group.id,
130
- accounts: [],
131
- metadata: { name: group.getDefaultName() },
132
- },
175
+ let wallet = wallets[walletId];
176
+ if (!wallet) {
177
+ wallets[walletId] = {
178
+ id: walletId,
179
+ category: rule.category,
180
+ groups: {},
181
+ metadata: {
182
+ name: '', // Will get updated later.
133
183
  },
184
+ };
185
+ wallet = wallets[walletId];
186
+ }
187
+ let group = wallet.groups[groupId];
188
+ if (!group) {
189
+ wallet.groups[groupId] = {
190
+ id: groupId,
191
+ accounts: [],
134
192
  metadata: {
135
- name: wallet.getDefaultName(),
193
+ name: '', // Will get updated later.
136
194
  },
137
195
  };
196
+ group = wallet.groups[groupId];
138
197
  }
139
- wallets[wallet.id].groups[group.id].accounts.push(account.id);
198
+ group.accounts.push(account.id);
199
+ // Update in-memory wallet/group instances.
200
+ __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, new AccountTreeWallet_1.AccountTreeWallet({ messenger: this.messagingSystem, wallet }));
140
201
  // Update the reverse mapping for this account.
141
- __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").set(account.id, {
202
+ __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
142
203
  walletId: wallet.id,
143
204
  groupId: group.id,
144
205
  });
@@ -1 +1 @@
1
- {"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"AccountTreeController.cjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletCategory } 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 {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers';\nimport type { AccountTreeGroup } from 'src';\n\nimport { AccountTreeWallet } from './AccountTreeWallet';\nimport type { Rule } from './rules';\nimport { EntropyRule, SnapRule, KeyringRule } from './rules';\nimport type { AccountContext } from './types';\n\nconst controllerName = 'AccountTreeController';\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 category: AccountWalletCategory;\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 | KeyringControllerStateChangeEvent;\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 #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #rules: Rule[];\n\n readonly #categoryToRule: Record<AccountWalletCategory, Rule>;\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.#accountIdToContext = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#categoryToRule = {\n [AccountWalletCategory.Entropy]: new EntropyRule(this.messagingSystem),\n [AccountWalletCategory.Snap]: new SnapRule(this.messagingSystem),\n [AccountWalletCategory.Keyring]: new KeyringRule(this.messagingSystem),\n } as const;\n this.#rules = [\n // 1. We group by entropy-source\n this.#categoryToRule[AccountWalletCategory.Entropy],\n // 2. We group by Snap ID\n this.#categoryToRule[AccountWalletCategory.Snap],\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n this.#categoryToRule[AccountWalletCategory.Keyring],\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: AccountTreeControllerState['accountTree']['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 // Once we have the account tree, we can compute the name.\n for (const wallet of Object.values(wallets)) {\n const walletInstance = this.getWallet(wallet.id);\n\n if (walletInstance) {\n if (wallet.metadata.name === '') {\n this.#renameAccountWallet(walletInstance, wallet);\n }\n\n for (const group of Object.values(wallet.groups)) {\n const groupInstance = walletInstance.getAccountGroup(group.id);\n\n if (groupInstance) {\n if (group.metadata.name === '') {\n this.#renameAccountGroup(groupInstance, group);\n }\n }\n }\n }\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n });\n }\n\n #renameAccountWallet(\n wallet: AccountTreeWallet,\n walletObject: AccountWalletObject,\n ) {\n const rule = this.#categoryToRule[walletObject.category];\n walletObject.metadata.name = rule.getDefaultAccountWalletName(wallet);\n }\n\n #renameAccountGroup(\n group: AccountTreeGroup,\n groupObject: AccountGroupObject,\n ) {\n const rule = this.#categoryToRule[group.wallet.category];\n groupObject.metadata.name = rule.getDefaultAccountGroupName(group);\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 const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n const walletInstance = this.getWallet(wallet.id);\n if (walletInstance) {\n if (wallet.metadata.name === '') {\n this.#renameAccountWallet(walletInstance, wallet);\n }\n\n const group = wallet.groups[groupId];\n if (group) {\n const groupInstance = walletInstance.getAccountGroup(group.id);\n if (groupInstance) {\n if (group.metadata.name === '') {\n this.#renameAccountGroup(groupInstance, group);\n }\n }\n }\n }\n }\n }\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\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: AccountTreeControllerState['accountTree']['wallets'],\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 { walletId, groupId } = match;\n\n // Update controller's state.\n let wallet = wallets[walletId];\n if (!wallet) {\n wallets[walletId] = {\n id: walletId,\n category: rule.category,\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n },\n };\n wallet = wallets[walletId];\n }\n\n let group = wallet.groups[groupId];\n if (!group) {\n wallet.groups[groupId] = {\n id: groupId,\n accounts: [],\n metadata: {\n name: '', // Will get updated later.\n },\n };\n group = wallet.groups[groupId];\n }\n\n group.accounts.push(account.id);\n\n // Update in-memory wallet/group instances.\n this.#wallets.set(\n wallet.id,\n new AccountTreeWallet({ messenger: this.messagingSystem, wallet }),\n );\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.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"]}
@@ -1,9 +1,10 @@
1
1
  import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
2
+ import { AccountWalletCategory } from "@metamask/account-api";
2
3
  import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
3
4
  import { type ControllerGetStateAction, type ControllerStateChangeEvent, type RestrictedMessenger, BaseController } from "@metamask/base-controller";
4
- import type { KeyringControllerGetStateAction } from "@metamask/keyring-controller";
5
+ import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent } from "@metamask/keyring-controller";
5
6
  import type { GetSnap as SnapControllerGetSnap } from "@metamask/snaps-controllers";
6
- import type { AccountTreeWallet } from "./AccountTreeWallet.cjs";
7
+ import { AccountTreeWallet } from "./AccountTreeWallet.cjs";
7
8
  declare const controllerName = "AccountTreeController";
8
9
  type Metadata = {
9
10
  name: string;
@@ -17,6 +18,7 @@ export type AccountGroupObject = {
17
18
  };
18
19
  export type AccountWalletObject = {
19
20
  id: AccountWalletId;
21
+ category: AccountWalletCategory;
20
22
  groups: {
21
23
  [groupId: AccountGroupId]: AccountGroupObject;
22
24
  };
@@ -33,7 +35,7 @@ export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeo
33
35
  export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
34
36
  export type AccountTreeControllerActions = never;
35
37
  export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
36
- export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
38
+ export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent | KeyringControllerStateChangeEvent;
37
39
  export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
38
40
  export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
39
41
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EAAE,+BAA+B,EAAE,qCAAqC;AAEpF,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,gCAA4B;AAQ7D,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAQ/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IAEpB,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IAwCD,IAAI;IAaJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CA6ElC"}
1
+ {"version":3,"file":"AccountTreeController.d.cts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,EAAE,qBAAqB,EAAE,8BAA8B;AAC9D,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EACV,+BAA+B,EAC/B,iCAAiC,EAClC,qCAAqC;AAEtC,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAGpF,OAAO,EAAE,iBAAiB,EAAE,gCAA4B;AAKxD,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAG/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,QAAQ,EAAE,qBAAqB,CAAC;IAEhC,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,GACrC,iCAAiC,CAAC;AAEtC,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IASC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IA6CD,IAAI;IAkDJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CAmHlC"}
@@ -1,9 +1,10 @@
1
1
  import type { AccountGroupId, AccountWalletId } from "@metamask/account-api";
2
+ import { AccountWalletCategory } from "@metamask/account-api";
2
3
  import type { AccountId, AccountsControllerAccountAddedEvent, AccountsControllerAccountRemovedEvent, AccountsControllerGetAccountAction, AccountsControllerListMultichainAccountsAction } from "@metamask/accounts-controller";
3
4
  import { type ControllerGetStateAction, type ControllerStateChangeEvent, type RestrictedMessenger, BaseController } from "@metamask/base-controller";
4
- import type { KeyringControllerGetStateAction } from "@metamask/keyring-controller";
5
+ import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent } from "@metamask/keyring-controller";
5
6
  import type { GetSnap as SnapControllerGetSnap } from "@metamask/snaps-controllers";
6
- import type { AccountTreeWallet } from "./AccountTreeWallet.mjs";
7
+ import { AccountTreeWallet } from "./AccountTreeWallet.mjs";
7
8
  declare const controllerName = "AccountTreeController";
8
9
  type Metadata = {
9
10
  name: string;
@@ -17,6 +18,7 @@ export type AccountGroupObject = {
17
18
  };
18
19
  export type AccountWalletObject = {
19
20
  id: AccountWalletId;
21
+ category: AccountWalletCategory;
20
22
  groups: {
21
23
  [groupId: AccountGroupId]: AccountGroupObject;
22
24
  };
@@ -33,7 +35,7 @@ export type AccountTreeControllerGetStateAction = ControllerGetStateAction<typeo
33
35
  export type AllowedActions = AccountsControllerGetAccountAction | AccountsControllerListMultichainAccountsAction | KeyringControllerGetStateAction | SnapControllerGetSnap;
34
36
  export type AccountTreeControllerActions = never;
35
37
  export type AccountTreeControllerStateChangeEvent = ControllerStateChangeEvent<typeof controllerName, AccountTreeControllerState>;
36
- export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent;
38
+ export type AllowedEvents = AccountsControllerAccountAddedEvent | AccountsControllerAccountRemovedEvent | KeyringControllerStateChangeEvent;
37
39
  export type AccountTreeControllerEvents = AccountTreeControllerStateChangeEvent;
38
40
  export type AccountTreeControllerMessenger = RestrictedMessenger<typeof controllerName, AccountTreeControllerActions | AllowedActions, AccountTreeControllerEvents | AllowedEvents, AllowedActions['type'], AllowedEvents['type']>;
39
41
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EAAE,+BAA+B,EAAE,qCAAqC;AAEpF,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAEpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,gCAA4B;AAQ7D,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAQ/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IAEpB,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,CAAC;AAE1C,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IAOC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IAwCD,IAAI;IAaJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CA6ElC"}
1
+ {"version":3,"file":"AccountTreeController.d.mts","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,8BAA8B;AAC7E,OAAO,EAAE,qBAAqB,EAAE,8BAA8B;AAC9D,OAAO,KAAK,EACV,SAAS,EACT,mCAAmC,EACnC,qCAAqC,EACrC,kCAAkC,EAClC,8CAA8C,EAC/C,sCAAsC;AAEvC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,mBAAmB,EACxB,cAAc,EACf,kCAAkC;AACnC,OAAO,KAAK,EACV,+BAA+B,EAC/B,iCAAiC,EAClC,qCAAqC;AAEtC,OAAO,KAAK,EAAE,OAAO,IAAI,qBAAqB,EAAE,oCAAoC;AAGpF,OAAO,EAAE,iBAAiB,EAAE,gCAA4B;AAKxD,QAAA,MAAM,cAAc,0BAA0B,CAAC;AAG/C,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,cAAc,CAAC;IAEnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,QAAQ,EAAE,oBAAoB,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,QAAQ,EAAE,qBAAqB,CAAC;IAEhC,MAAM,EAAE;QACN,CAAC,OAAO,EAAE,cAAc,GAAG,kBAAkB,CAAC;KAC/C,CAAC;IACF,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE;QACX,OAAO,EAAE;YAEP,CAAC,QAAQ,EAAE,eAAe,GAAG,mBAAmB,CAAC;SAClD,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB,kCAAkC,GAClC,8CAA8C,GAC9C,+BAA+B,GAC/B,qBAAqB,CAAC;AAE1B,MAAM,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,mCAAmC,GACnC,qCAAqC,GACrC,iCAAiC,CAAC;AAEtC,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF,MAAM,MAAM,8BAA8B,GAAG,mBAAmB,CAC9D,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,EAC3C,cAAc,CAAC,MAAM,CAAC,EACtB,aAAa,CAAC,MAAM,CAAC,CACtB,CAAC;AAUF;;;;GAIG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAMjF;AAED,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IASC;;;;;;OAMG;gBACS,EACV,SAAS,EACT,KAAK,GACN,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;KAC7C;IA6CD,IAAI;IAkDJ,SAAS,CAAC,EAAE,EAAE,eAAe,GAAG,iBAAiB,GAAG,SAAS;IAI7D,UAAU,IAAI,iBAAiB,EAAE;CAmHlC"}
@@ -9,9 +9,11 @@ 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_wallets, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
12
+ var _AccountTreeController_instances, _AccountTreeController_accountIdToContext, _AccountTreeController_rules, _AccountTreeController_categoryToRule, _AccountTreeController_wallets, _AccountTreeController_renameAccountWallet, _AccountTreeController_renameAccountGroup, _AccountTreeController_handleAccountAdded, _AccountTreeController_handleAccountRemoved, _AccountTreeController_insert, _AccountTreeController_listAccounts;
13
+ import { AccountWalletCategory } from "@metamask/account-api";
13
14
  import { BaseController } from "@metamask/base-controller";
14
- import { EntropySourceWalletRule, SnapWalletRule, KeyringWalletRule } from "./rules/index.mjs";
15
+ import { AccountTreeWallet } from "./AccountTreeWallet.mjs";
16
+ import { EntropyRule, SnapRule, KeyringRule } from "./rules/index.mjs";
15
17
  const controllerName = 'AccountTreeController';
16
18
  const accountTreeControllerMetadata = {
17
19
  accountTree: {
@@ -50,20 +52,26 @@ export class AccountTreeController extends BaseController {
50
52
  },
51
53
  });
52
54
  _AccountTreeController_instances.add(this);
53
- _AccountTreeController_reverse.set(this, void 0);
55
+ _AccountTreeController_accountIdToContext.set(this, void 0);
54
56
  _AccountTreeController_rules.set(this, void 0);
57
+ _AccountTreeController_categoryToRule.set(this, void 0);
55
58
  _AccountTreeController_wallets.set(this, void 0);
56
59
  __classPrivateFieldSet(this, _AccountTreeController_wallets, new Map(), "f");
57
60
  // Reverse map to allow fast node access from an account ID.
58
- __classPrivateFieldSet(this, _AccountTreeController_reverse, new Map(), "f");
61
+ __classPrivateFieldSet(this, _AccountTreeController_accountIdToContext, new Map(), "f");
59
62
  // Rules to apply to construct the wallets tree.
63
+ __classPrivateFieldSet(this, _AccountTreeController_categoryToRule, {
64
+ [AccountWalletCategory.Entropy]: new EntropyRule(this.messagingSystem),
65
+ [AccountWalletCategory.Snap]: new SnapRule(this.messagingSystem),
66
+ [AccountWalletCategory.Keyring]: new KeyringRule(this.messagingSystem),
67
+ }, "f");
60
68
  __classPrivateFieldSet(this, _AccountTreeController_rules, [
61
69
  // 1. We group by entropy-source
62
- new EntropySourceWalletRule(this.messagingSystem),
70
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[AccountWalletCategory.Entropy],
63
71
  // 2. We group by Snap ID
64
- new SnapWalletRule(this.messagingSystem),
72
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[AccountWalletCategory.Snap],
65
73
  // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)
66
- new KeyringWalletRule(this.messagingSystem),
74
+ __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[AccountWalletCategory.Keyring],
67
75
  ], "f");
68
76
  this.messagingSystem.subscribe('AccountsController:accountAdded', (account) => {
69
77
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_handleAccountAdded).call(this, account);
@@ -78,6 +86,23 @@ export class AccountTreeController extends BaseController {
78
86
  for (const account of __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_listAccounts).call(this)) {
79
87
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, wallets, account);
80
88
  }
89
+ // Once we have the account tree, we can compute the name.
90
+ for (const wallet of Object.values(wallets)) {
91
+ const walletInstance = this.getWallet(wallet.id);
92
+ if (walletInstance) {
93
+ if (wallet.metadata.name === '') {
94
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWallet).call(this, walletInstance, wallet);
95
+ }
96
+ for (const group of Object.values(wallet.groups)) {
97
+ const groupInstance = walletInstance.getAccountGroup(group.id);
98
+ if (groupInstance) {
99
+ if (group.metadata.name === '') {
100
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroup).call(this, groupInstance, group);
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
81
106
  this.update((state) => {
82
107
  state.accountTree.wallets = wallets;
83
108
  });
@@ -89,14 +114,42 @@ export class AccountTreeController extends BaseController {
89
114
  return Array.from(__classPrivateFieldGet(this, _AccountTreeController_wallets, "f").values());
90
115
  }
91
116
  }
92
- _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
117
+ _AccountTreeController_accountIdToContext = new WeakMap(), _AccountTreeController_rules = new WeakMap(), _AccountTreeController_categoryToRule = new WeakMap(), _AccountTreeController_wallets = new WeakMap(), _AccountTreeController_instances = new WeakSet(), _AccountTreeController_renameAccountWallet = function _AccountTreeController_renameAccountWallet(wallet, walletObject) {
118
+ const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[walletObject.category];
119
+ walletObject.metadata.name = rule.getDefaultAccountWalletName(wallet);
120
+ }, _AccountTreeController_renameAccountGroup = function _AccountTreeController_renameAccountGroup(group, groupObject) {
121
+ const rule = __classPrivateFieldGet(this, _AccountTreeController_categoryToRule, "f")[group.wallet.category];
122
+ groupObject.metadata.name = rule.getDefaultAccountGroupName(group);
123
+ }, _AccountTreeController_handleAccountAdded = function _AccountTreeController_handleAccountAdded(account) {
93
124
  this.update((state) => {
94
125
  __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_insert).call(this, state.accountTree.wallets, account);
126
+ const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(account.id);
127
+ if (context) {
128
+ const { walletId, groupId } = context;
129
+ const wallet = state.accountTree.wallets[walletId];
130
+ if (wallet) {
131
+ const walletInstance = this.getWallet(wallet.id);
132
+ if (walletInstance) {
133
+ if (wallet.metadata.name === '') {
134
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountWallet).call(this, walletInstance, wallet);
135
+ }
136
+ const group = wallet.groups[groupId];
137
+ if (group) {
138
+ const groupInstance = walletInstance.getAccountGroup(group.id);
139
+ if (groupInstance) {
140
+ if (group.metadata.name === '') {
141
+ __classPrivateFieldGet(this, _AccountTreeController_instances, "m", _AccountTreeController_renameAccountGroup).call(this, groupInstance, group);
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
95
148
  });
96
149
  }, _AccountTreeController_handleAccountRemoved = function _AccountTreeController_handleAccountRemoved(accountId) {
97
- const found = __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").get(accountId);
98
- if (found) {
99
- const { walletId, groupId } = found;
150
+ const context = __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").get(accountId);
151
+ if (context) {
152
+ const { walletId, groupId } = context;
100
153
  this.update((state) => {
101
154
  const { accounts } = state.accountTree.wallets[walletId].groups[groupId];
102
155
  const index = accounts.indexOf(accountId);
@@ -112,28 +165,36 @@ _AccountTreeController_reverse = new WeakMap(), _AccountTreeController_rules = n
112
165
  // No match for that rule, we go to the next one.
113
166
  continue;
114
167
  }
115
- const { wallet, group } = match;
116
- // Update in-memory wallet/group instances.
117
- __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, wallet);
168
+ const { walletId, groupId } = match;
118
169
  // Update controller's state.
119
- if (!wallets[wallet.id]) {
120
- wallets[wallet.id] = {
121
- id: wallet.id,
122
- groups: {
123
- [group.id]: {
124
- id: group.id,
125
- accounts: [],
126
- metadata: { name: group.getDefaultName() },
127
- },
170
+ let wallet = wallets[walletId];
171
+ if (!wallet) {
172
+ wallets[walletId] = {
173
+ id: walletId,
174
+ category: rule.category,
175
+ groups: {},
176
+ metadata: {
177
+ name: '', // Will get updated later.
128
178
  },
179
+ };
180
+ wallet = wallets[walletId];
181
+ }
182
+ let group = wallet.groups[groupId];
183
+ if (!group) {
184
+ wallet.groups[groupId] = {
185
+ id: groupId,
186
+ accounts: [],
129
187
  metadata: {
130
- name: wallet.getDefaultName(),
188
+ name: '', // Will get updated later.
131
189
  },
132
190
  };
191
+ group = wallet.groups[groupId];
133
192
  }
134
- wallets[wallet.id].groups[group.id].accounts.push(account.id);
193
+ group.accounts.push(account.id);
194
+ // Update in-memory wallet/group instances.
195
+ __classPrivateFieldGet(this, _AccountTreeController_wallets, "f").set(wallet.id, new AccountTreeWallet({ messenger: this.messagingSystem, wallet }));
135
196
  // Update the reverse mapping for this account.
136
- __classPrivateFieldGet(this, _AccountTreeController_reverse, "f").set(account.id, {
197
+ __classPrivateFieldGet(this, _AccountTreeController_accountIdToContext, "f").set(account.id, {
137
198
  walletId: wallet.id,
138
199
  groupId: group.id,
139
200
  });
@@ -1 +1 @@
1
- {"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"AccountTreeController.mjs","sourceRoot":"","sources":["../src/AccountTreeController.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountGroupId, AccountWalletId } from '@metamask/account-api';\nimport { AccountWalletCategory } 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 {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n} from '@metamask/keyring-controller';\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport type { GetSnap as SnapControllerGetSnap } from '@metamask/snaps-controllers';\nimport type { AccountTreeGroup } from 'src';\n\nimport { AccountTreeWallet } from './AccountTreeWallet';\nimport type { Rule } from './rules';\nimport { EntropyRule, SnapRule, KeyringRule } from './rules';\nimport type { AccountContext } from './types';\n\nconst controllerName = 'AccountTreeController';\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 category: AccountWalletCategory;\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 | KeyringControllerStateChangeEvent;\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 #accountIdToContext: Map<AccountId, AccountContext>;\n\n readonly #rules: Rule[];\n\n readonly #categoryToRule: Record<AccountWalletCategory, Rule>;\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.#accountIdToContext = new Map();\n\n // Rules to apply to construct the wallets tree.\n this.#categoryToRule = {\n [AccountWalletCategory.Entropy]: new EntropyRule(this.messagingSystem),\n [AccountWalletCategory.Snap]: new SnapRule(this.messagingSystem),\n [AccountWalletCategory.Keyring]: new KeyringRule(this.messagingSystem),\n } as const;\n this.#rules = [\n // 1. We group by entropy-source\n this.#categoryToRule[AccountWalletCategory.Entropy],\n // 2. We group by Snap ID\n this.#categoryToRule[AccountWalletCategory.Snap],\n // 3. We group by wallet type (this rule cannot fail and will group all non-matching accounts)\n this.#categoryToRule[AccountWalletCategory.Keyring],\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: AccountTreeControllerState['accountTree']['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 // Once we have the account tree, we can compute the name.\n for (const wallet of Object.values(wallets)) {\n const walletInstance = this.getWallet(wallet.id);\n\n if (walletInstance) {\n if (wallet.metadata.name === '') {\n this.#renameAccountWallet(walletInstance, wallet);\n }\n\n for (const group of Object.values(wallet.groups)) {\n const groupInstance = walletInstance.getAccountGroup(group.id);\n\n if (groupInstance) {\n if (group.metadata.name === '') {\n this.#renameAccountGroup(groupInstance, group);\n }\n }\n }\n }\n }\n\n this.update((state) => {\n state.accountTree.wallets = wallets;\n });\n }\n\n #renameAccountWallet(\n wallet: AccountTreeWallet,\n walletObject: AccountWalletObject,\n ) {\n const rule = this.#categoryToRule[walletObject.category];\n walletObject.metadata.name = rule.getDefaultAccountWalletName(wallet);\n }\n\n #renameAccountGroup(\n group: AccountTreeGroup,\n groupObject: AccountGroupObject,\n ) {\n const rule = this.#categoryToRule[group.wallet.category];\n groupObject.metadata.name = rule.getDefaultAccountGroupName(group);\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 const context = this.#accountIdToContext.get(account.id);\n if (context) {\n const { walletId, groupId } = context;\n\n const wallet = state.accountTree.wallets[walletId];\n if (wallet) {\n const walletInstance = this.getWallet(wallet.id);\n if (walletInstance) {\n if (wallet.metadata.name === '') {\n this.#renameAccountWallet(walletInstance, wallet);\n }\n\n const group = wallet.groups[groupId];\n if (group) {\n const groupInstance = walletInstance.getAccountGroup(group.id);\n if (groupInstance) {\n if (group.metadata.name === '') {\n this.#renameAccountGroup(groupInstance, group);\n }\n }\n }\n }\n }\n }\n });\n }\n\n #handleAccountRemoved(accountId: AccountId) {\n const context = this.#accountIdToContext.get(accountId);\n\n if (context) {\n const { walletId, groupId } = context;\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: AccountTreeControllerState['accountTree']['wallets'],\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 { walletId, groupId } = match;\n\n // Update controller's state.\n let wallet = wallets[walletId];\n if (!wallet) {\n wallets[walletId] = {\n id: walletId,\n category: rule.category,\n groups: {},\n metadata: {\n name: '', // Will get updated later.\n },\n };\n wallet = wallets[walletId];\n }\n\n let group = wallet.groups[groupId];\n if (!group) {\n wallet.groups[groupId] = {\n id: groupId,\n accounts: [],\n metadata: {\n name: '', // Will get updated later.\n },\n };\n group = wallet.groups[groupId];\n }\n\n group.accounts.push(account.id);\n\n // Update in-memory wallet/group instances.\n this.#wallets.set(\n wallet.id,\n new AccountTreeWallet({ messenger: this.messagingSystem, wallet }),\n );\n\n // Update the reverse mapping for this account.\n this.#accountIdToContext.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"]}