@metamask/accounts-controller 18.2.0 → 18.2.2
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +37 -1
- package/dist/AccountsController.cjs +608 -0
- package/dist/AccountsController.cjs.map +1 -0
- package/dist/{types/AccountsController.d.ts → AccountsController.d.cts} +8 -8
- package/dist/AccountsController.d.cts.map +1 -0
- package/dist/AccountsController.d.mts +215 -0
- package/dist/AccountsController.d.mts.map +1 -0
- package/dist/AccountsController.mjs +604 -9
- package/dist/AccountsController.mjs.map +1 -1
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{types/index.d.ts → index.d.cts} +4 -4
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -13
- package/dist/index.mjs.map +1 -1
- package/dist/tests/mocks.cjs +49 -0
- package/dist/tests/mocks.cjs.map +1 -0
- package/dist/{types/tests/mocks.d.ts → tests/mocks.d.cts} +3 -3
- package/dist/tests/mocks.d.cts.map +1 -0
- package/dist/tests/mocks.d.mts +17 -0
- package/dist/tests/mocks.d.mts.map +1 -0
- package/dist/tests/mocks.mjs +41 -60
- package/dist/tests/mocks.mjs.map +1 -1
- package/dist/utils.cjs +80 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/{types/utils.d.ts → utils.d.cts} +3 -3
- package/dist/utils.d.cts.map +1 -0
- package/dist/utils.d.mts +28 -0
- package/dist/utils.d.mts.map +1 -0
- package/dist/utils.mjs +73 -13
- package/dist/utils.mjs.map +1 -1
- package/package.json +20 -15
- package/dist/AccountsController.js +0 -11
- package/dist/AccountsController.js.map +0 -1
- package/dist/chunk-2DVFC4VN.js +0 -763
- package/dist/chunk-2DVFC4VN.js.map +0 -1
- package/dist/chunk-BYPP7G2N.js +0 -56
- package/dist/chunk-BYPP7G2N.js.map +0 -1
- package/dist/chunk-RIZO66PK.mjs +0 -763
- package/dist/chunk-RIZO66PK.mjs.map +0 -1
- package/dist/chunk-UJIPPGP6.js +0 -19
- package/dist/chunk-UJIPPGP6.js.map +0 -1
- package/dist/chunk-Y2QVUNIA.mjs +0 -56
- package/dist/chunk-Y2QVUNIA.mjs.map +0 -1
- package/dist/chunk-ZNSHBDHA.mjs +0 -19
- package/dist/chunk-ZNSHBDHA.mjs.map +0 -1
- package/dist/index.js +0 -14
- package/dist/index.js.map +0 -1
- package/dist/tests/mocks.js +0 -65
- package/dist/tests/mocks.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/types/AccountsController.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/tests/mocks.d.ts.map +0 -1
- package/dist/types/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -14
- package/dist/utils.js.map +0 -1
package/CHANGELOG.md
CHANGED
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [18.2.2]
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Bump accounts related packages ([#4713](https://github.com/MetaMask/core/pull/4713)), ([#4728](https://github.com/MetaMask/core/pull/4728))
|
15
|
+
- Those packages are now built slightly differently and are part of the [accounts monorepo](https://github.com/MetaMask/accounts).
|
16
|
+
- Bump `@metamask/keyring-api` from `^8.1.0` to `^8.1.4`
|
17
|
+
- Bump `@metamask/eth-snap-keyring` from `^4.3.3` to `^4.3.6`
|
18
|
+
|
19
|
+
## [18.2.1]
|
20
|
+
|
21
|
+
### Changed
|
22
|
+
|
23
|
+
- Bump `@metamask/eth-snap-keyring` from `^4.3.1` to `^4.3.3` ([#4689](https://github.com/MetaMask/core/pull/4689))
|
24
|
+
- Bump `@metamask/snaps-sdk` from `^6.1.1` to `^6.5.0` ([#4689](https://github.com/MetaMask/core/pull/4689))
|
25
|
+
- Bump `@metamask/snaps-utils` from `^7.8.1` to `^8.1.1` ([#4689](https://github.com/MetaMask/core/pull/4689))
|
26
|
+
- Bump peer dependency `@metamask/snaps-controllers` from `^9.3.0` to `^9.7.0` ([#4689](https://github.com/MetaMask/core/pull/4689))
|
27
|
+
|
28
|
+
### Fixed
|
29
|
+
|
30
|
+
- Produce and export ESM-compatible TypeScript type declaration files in addition to CommonJS-compatible declaration files ([#4648](https://github.com/MetaMask/core/pull/4648))
|
31
|
+
- Previously, this package shipped with only one variant of type declaration
|
32
|
+
files, and these files were only CommonJS-compatible, and the `exports`
|
33
|
+
field in `package.json` linked to these files. This is an anti-pattern and
|
34
|
+
was rightfully flagged by the
|
35
|
+
["Are the Types Wrong?"](https://arethetypeswrong.github.io/) tool as
|
36
|
+
["masquerading as CJS"](https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md).
|
37
|
+
All of the ATTW checks now pass.
|
38
|
+
- Remove chunk files ([#4648](https://github.com/MetaMask/core/pull/4648)).
|
39
|
+
- Previously, the build tool we used to generate JavaScript files extracted
|
40
|
+
common code to "chunk" files. While this was intended to make this package
|
41
|
+
more tree-shakeable, it also made debugging more difficult for our
|
42
|
+
development teams. These chunk files are no longer present.
|
43
|
+
|
10
44
|
## [18.2.0]
|
11
45
|
|
12
46
|
### Added
|
@@ -295,7 +329,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
295
329
|
|
296
330
|
- Initial release ([#1637](https://github.com/MetaMask/core/pull/1637))
|
297
331
|
|
298
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.
|
332
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.2...HEAD
|
333
|
+
[18.2.2]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.1...@metamask/accounts-controller@18.2.2
|
334
|
+
[18.2.1]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.2.0...@metamask/accounts-controller@18.2.1
|
299
335
|
[18.2.0]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.1.1...@metamask/accounts-controller@18.2.0
|
300
336
|
[18.1.1]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.1.0...@metamask/accounts-controller@18.1.1
|
301
337
|
[18.1.0]: https://github.com/MetaMask/core/compare/@metamask/accounts-controller@18.0.0...@metamask/accounts-controller@18.1.0
|
@@ -0,0 +1,608 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
4
|
+
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");
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
6
|
+
};
|
7
|
+
var _AccountsController_instances, _AccountsController_generateInternalAccountForNonSnapAccount, _AccountsController_listSnapAccounts, _AccountsController_listNormalAccounts, _AccountsController_handleOnKeyringStateChange, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_isAccountCompatibleWithChain, _AccountsController_getLastSelectedIndex, _AccountsController_handleNewAccountAdded, _AccountsController_publishAccountChangeEvent, _AccountsController_handleAccountRemoved, _AccountsController_populateExistingMetadata, _AccountsController_registerMessageHandlers;
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
9
|
+
exports.AccountsController = exports.EMPTY_ACCOUNT = void 0;
|
10
|
+
const base_controller_1 = require("@metamask/base-controller");
|
11
|
+
const eth_snap_keyring_1 = require("@metamask/eth-snap-keyring");
|
12
|
+
const keyring_api_1 = require("@metamask/keyring-api");
|
13
|
+
const keyring_controller_1 = require("@metamask/keyring-controller");
|
14
|
+
const utils_1 = require("@metamask/utils");
|
15
|
+
const utils_2 = require("./utils.cjs");
|
16
|
+
const controllerName = 'AccountsController';
|
17
|
+
const accountsControllerMetadata = {
|
18
|
+
internalAccounts: {
|
19
|
+
persist: true,
|
20
|
+
anonymous: false,
|
21
|
+
},
|
22
|
+
};
|
23
|
+
const defaultState = {
|
24
|
+
internalAccounts: {
|
25
|
+
accounts: {},
|
26
|
+
selectedAccount: '',
|
27
|
+
},
|
28
|
+
};
|
29
|
+
exports.EMPTY_ACCOUNT = {
|
30
|
+
id: '',
|
31
|
+
address: '',
|
32
|
+
options: {},
|
33
|
+
methods: [],
|
34
|
+
type: keyring_api_1.EthAccountType.Eoa,
|
35
|
+
metadata: {
|
36
|
+
name: '',
|
37
|
+
keyring: {
|
38
|
+
type: '',
|
39
|
+
},
|
40
|
+
importTime: 0,
|
41
|
+
},
|
42
|
+
};
|
43
|
+
/**
|
44
|
+
* Controller that manages internal accounts.
|
45
|
+
* The accounts controller is responsible for creating and managing internal accounts.
|
46
|
+
* It also provides convenience methods for accessing and updating the internal accounts.
|
47
|
+
* The accounts controller also listens for keyring state changes and updates the internal accounts accordingly.
|
48
|
+
* The accounts controller also listens for snap state changes and updates the internal accounts accordingly.
|
49
|
+
*
|
50
|
+
*/
|
51
|
+
class AccountsController extends base_controller_1.BaseController {
|
52
|
+
/**
|
53
|
+
* Constructor for AccountsController.
|
54
|
+
*
|
55
|
+
* @param options - The controller options.
|
56
|
+
* @param options.messenger - The messenger object.
|
57
|
+
* @param options.state - Initial state to set on this controller
|
58
|
+
*/
|
59
|
+
constructor({ messenger, state, }) {
|
60
|
+
super({
|
61
|
+
messenger,
|
62
|
+
name: controllerName,
|
63
|
+
metadata: accountsControllerMetadata,
|
64
|
+
state: {
|
65
|
+
...defaultState,
|
66
|
+
...state,
|
67
|
+
},
|
68
|
+
});
|
69
|
+
_AccountsController_instances.add(this);
|
70
|
+
this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
|
71
|
+
this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));
|
72
|
+
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_registerMessageHandlers).call(this);
|
73
|
+
}
|
74
|
+
/**
|
75
|
+
* Returns the internal account object for the given account ID, if it exists.
|
76
|
+
*
|
77
|
+
* @param accountId - The ID of the account to retrieve.
|
78
|
+
* @returns The internal account object, or undefined if the account does not exist.
|
79
|
+
*/
|
80
|
+
getAccount(accountId) {
|
81
|
+
return this.state.internalAccounts.accounts[accountId];
|
82
|
+
}
|
83
|
+
/**
|
84
|
+
* Returns an array of all evm internal accounts.
|
85
|
+
*
|
86
|
+
* @returns An array of InternalAccount objects.
|
87
|
+
*/
|
88
|
+
listAccounts() {
|
89
|
+
const accounts = Object.values(this.state.internalAccounts.accounts);
|
90
|
+
return accounts.filter((account) => (0, keyring_api_1.isEvmAccountType)(account.type));
|
91
|
+
}
|
92
|
+
/**
|
93
|
+
* Returns an array of all internal accounts.
|
94
|
+
*
|
95
|
+
* @param chainId - The chain ID.
|
96
|
+
* @returns An array of InternalAccount objects.
|
97
|
+
*/
|
98
|
+
listMultichainAccounts(chainId) {
|
99
|
+
const accounts = Object.values(this.state.internalAccounts.accounts);
|
100
|
+
if (!chainId) {
|
101
|
+
return accounts;
|
102
|
+
}
|
103
|
+
if (!(0, utils_1.isCaipChainId)(chainId)) {
|
104
|
+
throw new Error(`Invalid CAIP-2 chain ID: ${String(chainId)}`);
|
105
|
+
}
|
106
|
+
return accounts.filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
|
107
|
+
}
|
108
|
+
/**
|
109
|
+
* Returns the internal account object for the given account ID.
|
110
|
+
*
|
111
|
+
* @param accountId - The ID of the account to retrieve.
|
112
|
+
* @returns The internal account object.
|
113
|
+
* @throws An error if the account ID is not found.
|
114
|
+
*/
|
115
|
+
getAccountExpect(accountId) {
|
116
|
+
const account = this.getAccount(accountId);
|
117
|
+
if (account === undefined) {
|
118
|
+
throw new Error(`Account Id "${accountId}" not found`);
|
119
|
+
}
|
120
|
+
return account;
|
121
|
+
}
|
122
|
+
/**
|
123
|
+
* Returns the last selected EVM account.
|
124
|
+
*
|
125
|
+
* @returns The selected internal account.
|
126
|
+
*/
|
127
|
+
getSelectedAccount() {
|
128
|
+
// Edge case where the extension is setup but the srp is not yet created
|
129
|
+
// certain ui elements will query the selected address before any accounts are created.
|
130
|
+
if (this.state.internalAccounts.selectedAccount === '') {
|
131
|
+
return exports.EMPTY_ACCOUNT;
|
132
|
+
}
|
133
|
+
const selectedAccount = this.getAccountExpect(this.state.internalAccounts.selectedAccount);
|
134
|
+
if ((0, keyring_api_1.isEvmAccountType)(selectedAccount.type)) {
|
135
|
+
return selectedAccount;
|
136
|
+
}
|
137
|
+
const accounts = this.listAccounts();
|
138
|
+
if (!accounts.length) {
|
139
|
+
// ! Should never reach this.
|
140
|
+
throw new Error('No EVM accounts');
|
141
|
+
}
|
142
|
+
// This will never be undefined because we have already checked if accounts.length is > 0
|
143
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
144
|
+
return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
|
145
|
+
}
|
146
|
+
/**
|
147
|
+
* __WARNING The return value may be undefined if there isn't an account for that chain id.__
|
148
|
+
*
|
149
|
+
* Retrieves the last selected account by chain ID.
|
150
|
+
*
|
151
|
+
* @param chainId - The chain ID to filter the accounts.
|
152
|
+
* @returns The last selected account compatible with the specified chain ID or undefined.
|
153
|
+
*/
|
154
|
+
getSelectedMultichainAccount(chainId) {
|
155
|
+
// Edge case where the extension is setup but the srp is not yet created
|
156
|
+
// certain ui elements will query the selected address before any accounts are created.
|
157
|
+
if (this.state.internalAccounts.selectedAccount === '') {
|
158
|
+
return exports.EMPTY_ACCOUNT;
|
159
|
+
}
|
160
|
+
if (!chainId) {
|
161
|
+
return this.getAccountExpect(this.state.internalAccounts.selectedAccount);
|
162
|
+
}
|
163
|
+
if (!(0, utils_1.isCaipChainId)(chainId)) {
|
164
|
+
throw new Error(`Invalid CAIP-2 chain ID: ${chainId}`);
|
165
|
+
}
|
166
|
+
const accounts = Object.values(this.state.internalAccounts.accounts).filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
|
167
|
+
return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
|
168
|
+
}
|
169
|
+
/**
|
170
|
+
* Returns the account with the specified address.
|
171
|
+
* ! This method will only return the first account that matches the address
|
172
|
+
* @param address - The address of the account to retrieve.
|
173
|
+
* @returns The account with the specified address, or undefined if not found.
|
174
|
+
*/
|
175
|
+
getAccountByAddress(address) {
|
176
|
+
return this.listMultichainAccounts().find((account) => account.address.toLowerCase() === address.toLowerCase());
|
177
|
+
}
|
178
|
+
/**
|
179
|
+
* Sets the selected account by its ID.
|
180
|
+
*
|
181
|
+
* @param accountId - The ID of the account to be selected.
|
182
|
+
*/
|
183
|
+
setSelectedAccount(accountId) {
|
184
|
+
const account = this.getAccountExpect(accountId);
|
185
|
+
this.update((currentState) => {
|
186
|
+
currentState.internalAccounts.accounts[account.id].metadata.lastSelected =
|
187
|
+
Date.now();
|
188
|
+
currentState.internalAccounts.selectedAccount = account.id;
|
189
|
+
});
|
190
|
+
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, account);
|
191
|
+
}
|
192
|
+
/**
|
193
|
+
* Sets the name of the account with the given ID.
|
194
|
+
*
|
195
|
+
* @param accountId - The ID of the account to set the name for.
|
196
|
+
* @param accountName - The new name for the account.
|
197
|
+
* @throws An error if an account with the same name already exists.
|
198
|
+
*/
|
199
|
+
setAccountName(accountId, accountName) {
|
200
|
+
// This will check for name uniqueness and fire the `accountRenamed` event
|
201
|
+
// if the account has been renamed.
|
202
|
+
this.updateAccountMetadata(accountId, {
|
203
|
+
name: accountName,
|
204
|
+
nameLastUpdatedAt: Date.now(),
|
205
|
+
});
|
206
|
+
}
|
207
|
+
/**
|
208
|
+
* Updates the metadata of the account with the given ID.
|
209
|
+
*
|
210
|
+
* @param accountId - The ID of the account for which the metadata will be updated.
|
211
|
+
* @param metadata - The new metadata for the account.
|
212
|
+
*/
|
213
|
+
updateAccountMetadata(accountId, metadata) {
|
214
|
+
const account = this.getAccountExpect(accountId);
|
215
|
+
if (metadata.name &&
|
216
|
+
this.listMultichainAccounts().find((internalAccount) => internalAccount.metadata.name === metadata.name &&
|
217
|
+
internalAccount.id !== accountId)) {
|
218
|
+
throw new Error('Account name already exists');
|
219
|
+
}
|
220
|
+
this.update((currentState) => {
|
221
|
+
const internalAccount = {
|
222
|
+
...account,
|
223
|
+
metadata: { ...account.metadata, ...metadata },
|
224
|
+
};
|
225
|
+
// Do not remove this comment - This error is flaky: Comment out or restore the `ts-expect-error` directive below as needed.
|
226
|
+
// See: https://github.com/MetaMask/utils/issues/168
|
227
|
+
// // @ts-expect-error Known issue - `Json` causes recursive error in immer `Draft`/`WritableDraft` types
|
228
|
+
currentState.internalAccounts.accounts[accountId] = internalAccount;
|
229
|
+
if (metadata.name) {
|
230
|
+
this.messagingSystem.publish('AccountsController:accountRenamed', internalAccount);
|
231
|
+
}
|
232
|
+
});
|
233
|
+
}
|
234
|
+
/**
|
235
|
+
* Updates the internal accounts list by retrieving normal and snap accounts,
|
236
|
+
* removing duplicates, and updating the metadata of each account.
|
237
|
+
*
|
238
|
+
* @returns A Promise that resolves when the accounts have been updated.
|
239
|
+
*/
|
240
|
+
async updateAccounts() {
|
241
|
+
const snapAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listSnapAccounts).call(this);
|
242
|
+
const normalAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listNormalAccounts).call(this);
|
243
|
+
// keyring type map.
|
244
|
+
const keyringTypes = new Map();
|
245
|
+
const previousAccounts = this.state.internalAccounts.accounts;
|
246
|
+
const accounts = [
|
247
|
+
...normalAccounts,
|
248
|
+
...snapAccounts,
|
249
|
+
].reduce((internalAccountMap, internalAccount) => {
|
250
|
+
const keyringTypeName = (0, utils_2.keyringTypeToName)(internalAccount.metadata.keyring.type);
|
251
|
+
const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;
|
252
|
+
if (keyringAccountIndex) {
|
253
|
+
keyringTypes.set(keyringTypeName, keyringAccountIndex + 1);
|
254
|
+
}
|
255
|
+
else {
|
256
|
+
keyringTypes.set(keyringTypeName, 1);
|
257
|
+
}
|
258
|
+
const existingAccount = previousAccounts[internalAccount.id];
|
259
|
+
internalAccountMap[internalAccount.id] = {
|
260
|
+
...internalAccount,
|
261
|
+
metadata: {
|
262
|
+
...internalAccount.metadata,
|
263
|
+
name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'name') ??
|
264
|
+
`${keyringTypeName} ${keyringAccountIndex + 1}`,
|
265
|
+
importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'importTime') ??
|
266
|
+
Date.now(),
|
267
|
+
lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'lastSelected') ?? 0,
|
268
|
+
},
|
269
|
+
};
|
270
|
+
return internalAccountMap;
|
271
|
+
}, {});
|
272
|
+
this.update((currentState) => {
|
273
|
+
currentState.internalAccounts.accounts = accounts;
|
274
|
+
if (!currentState.internalAccounts.accounts[currentState.internalAccounts.selectedAccount]) {
|
275
|
+
const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, Object.values(accounts));
|
276
|
+
if (lastSelectedAccount) {
|
277
|
+
currentState.internalAccounts.selectedAccount =
|
278
|
+
lastSelectedAccount.id;
|
279
|
+
currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
|
280
|
+
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
|
281
|
+
}
|
282
|
+
else {
|
283
|
+
// It will be undefined if there are no accounts
|
284
|
+
currentState.internalAccounts.selectedAccount = '';
|
285
|
+
}
|
286
|
+
}
|
287
|
+
});
|
288
|
+
}
|
289
|
+
/**
|
290
|
+
* Loads the backup state of the accounts controller.
|
291
|
+
*
|
292
|
+
* @param backup - The backup state to load.
|
293
|
+
*/
|
294
|
+
loadBackup(backup) {
|
295
|
+
if (backup.internalAccounts) {
|
296
|
+
this.update((currentState) => {
|
297
|
+
currentState.internalAccounts = backup.internalAccounts;
|
298
|
+
});
|
299
|
+
}
|
300
|
+
}
|
301
|
+
/**
|
302
|
+
* Returns the next account number for a given keyring type.
|
303
|
+
* @param keyringType - The type of keyring.
|
304
|
+
* @param accounts - Existing accounts to check for the next available account number.
|
305
|
+
* @returns An object containing the account prefix and index to use.
|
306
|
+
*/
|
307
|
+
getNextAvailableAccountName(keyringType = keyring_controller_1.KeyringTypes.hd, accounts) {
|
308
|
+
const keyringName = (0, utils_2.keyringTypeToName)(keyringType);
|
309
|
+
const keyringAccounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getAccountsByKeyringType).call(this, keyringType, accounts);
|
310
|
+
const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce((maxInternalAccountIndex, internalAccount) => {
|
311
|
+
// We **DO NOT USE** `\d+` here to only consider valid "human"
|
312
|
+
// number (rounded decimal number)
|
313
|
+
const match = new RegExp(`${keyringName} ([0-9]+)$`, 'u').exec(internalAccount.metadata.name);
|
314
|
+
if (match) {
|
315
|
+
// Quoting `RegExp.exec` documentation:
|
316
|
+
// > The returned array has the matched text as the first item, and then one item for
|
317
|
+
// > each capturing group of the matched text.
|
318
|
+
// So use `match[1]` to get the captured value
|
319
|
+
const internalAccountIndex = parseInt(match[1], 10);
|
320
|
+
return Math.max(maxInternalAccountIndex, internalAccountIndex);
|
321
|
+
}
|
322
|
+
return maxInternalAccountIndex;
|
323
|
+
}, 0);
|
324
|
+
const index = Math.max(keyringAccounts.length + 1,
|
325
|
+
// ESLint is confused; this is a number.
|
326
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
327
|
+
lastDefaultIndexUsedForKeyringType + 1);
|
328
|
+
return `${keyringName} ${index}`;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
exports.AccountsController = AccountsController;
|
332
|
+
_AccountsController_instances = new WeakSet(), _AccountsController_generateInternalAccountForNonSnapAccount = function _AccountsController_generateInternalAccountForNonSnapAccount(address, type) {
|
333
|
+
return {
|
334
|
+
id: (0, utils_2.getUUIDFromAddressOfNormalAccount)(address),
|
335
|
+
address,
|
336
|
+
options: {},
|
337
|
+
methods: [
|
338
|
+
keyring_api_1.EthMethod.PersonalSign,
|
339
|
+
keyring_api_1.EthMethod.Sign,
|
340
|
+
keyring_api_1.EthMethod.SignTransaction,
|
341
|
+
keyring_api_1.EthMethod.SignTypedDataV1,
|
342
|
+
keyring_api_1.EthMethod.SignTypedDataV3,
|
343
|
+
keyring_api_1.EthMethod.SignTypedDataV4,
|
344
|
+
],
|
345
|
+
type: keyring_api_1.EthAccountType.Eoa,
|
346
|
+
metadata: {
|
347
|
+
name: '',
|
348
|
+
importTime: Date.now(),
|
349
|
+
keyring: {
|
350
|
+
type,
|
351
|
+
},
|
352
|
+
},
|
353
|
+
};
|
354
|
+
}, _AccountsController_listSnapAccounts =
|
355
|
+
/**
|
356
|
+
* Returns a list of internal accounts created using the SnapKeyring.
|
357
|
+
*
|
358
|
+
* @returns A promise that resolves to an array of InternalAccount objects.
|
359
|
+
*/
|
360
|
+
async function _AccountsController_listSnapAccounts() {
|
361
|
+
const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
|
362
|
+
// snap keyring is not available until the first account is created in the keyring controller
|
363
|
+
if (!snapKeyring) {
|
364
|
+
return [];
|
365
|
+
}
|
366
|
+
const snapAccounts = snapKeyring.listAccounts();
|
367
|
+
return snapAccounts;
|
368
|
+
}, _AccountsController_listNormalAccounts =
|
369
|
+
/**
|
370
|
+
* Returns a list of normal accounts.
|
371
|
+
* Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
|
372
|
+
* Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.
|
373
|
+
*
|
374
|
+
* @returns A Promise that resolves to an array of InternalAccount objects.
|
375
|
+
*/
|
376
|
+
async function _AccountsController_listNormalAccounts() {
|
377
|
+
const addresses = await this.messagingSystem.call('KeyringController:getAccounts');
|
378
|
+
const internalAccounts = [];
|
379
|
+
for (const address of addresses) {
|
380
|
+
const keyring = await this.messagingSystem.call('KeyringController:getKeyringForAccount', address);
|
381
|
+
const keyringType = keyring.type;
|
382
|
+
if (!(0, utils_2.isNormalKeyringType)(keyringType)) {
|
383
|
+
// We only consider "normal accounts" here, so keep looping
|
384
|
+
continue;
|
385
|
+
}
|
386
|
+
const id = (0, utils_2.getUUIDFromAddressOfNormalAccount)(address);
|
387
|
+
const nameLastUpdatedAt = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'nameLastUpdatedAt');
|
388
|
+
internalAccounts.push({
|
389
|
+
id,
|
390
|
+
address,
|
391
|
+
options: {},
|
392
|
+
methods: [
|
393
|
+
keyring_api_1.EthMethod.PersonalSign,
|
394
|
+
keyring_api_1.EthMethod.Sign,
|
395
|
+
keyring_api_1.EthMethod.SignTransaction,
|
396
|
+
keyring_api_1.EthMethod.SignTypedDataV1,
|
397
|
+
keyring_api_1.EthMethod.SignTypedDataV3,
|
398
|
+
keyring_api_1.EthMethod.SignTypedDataV4,
|
399
|
+
],
|
400
|
+
type: keyring_api_1.EthAccountType.Eoa,
|
401
|
+
metadata: {
|
402
|
+
name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'name') ?? '',
|
403
|
+
...(nameLastUpdatedAt && { nameLastUpdatedAt }),
|
404
|
+
importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'importTime') ?? Date.now(),
|
405
|
+
lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'lastSelected') ?? 0,
|
406
|
+
keyring: {
|
407
|
+
type: keyring.type,
|
408
|
+
},
|
409
|
+
},
|
410
|
+
});
|
411
|
+
}
|
412
|
+
return internalAccounts;
|
413
|
+
}, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange(keyringState) {
|
414
|
+
// check if there are any new accounts added
|
415
|
+
// TODO: change when accountAdded event is added to the keyring controller
|
416
|
+
// We check for keyrings length to be greater than 0 because the extension client may try execute
|
417
|
+
// submit password twice and clear the keyring state.
|
418
|
+
// https://github.com/MetaMask/KeyringController/blob/2d73a4deed8d013913f6ef0c9f5c0bb7c614f7d3/src/KeyringController.ts#L910
|
419
|
+
if (keyringState.isUnlocked && keyringState.keyrings.length > 0) {
|
420
|
+
const updatedNormalKeyringAddresses = [];
|
421
|
+
const updatedSnapKeyringAddresses = [];
|
422
|
+
for (const keyring of keyringState.keyrings) {
|
423
|
+
if (keyring.type === keyring_controller_1.KeyringTypes.snap) {
|
424
|
+
updatedSnapKeyringAddresses.push(...keyring.accounts.map((address) => {
|
425
|
+
return {
|
426
|
+
address,
|
427
|
+
type: keyring.type,
|
428
|
+
};
|
429
|
+
}));
|
430
|
+
}
|
431
|
+
else {
|
432
|
+
updatedNormalKeyringAddresses.push(...keyring.accounts.map((address) => {
|
433
|
+
return {
|
434
|
+
address,
|
435
|
+
type: keyring.type,
|
436
|
+
};
|
437
|
+
}));
|
438
|
+
}
|
439
|
+
}
|
440
|
+
const { previousNormalInternalAccounts, previousSnapInternalAccounts } = this.listMultichainAccounts().reduce((accumulator, account) => {
|
441
|
+
if (account.metadata.keyring.type === keyring_controller_1.KeyringTypes.snap) {
|
442
|
+
accumulator.previousSnapInternalAccounts.push(account);
|
443
|
+
}
|
444
|
+
else {
|
445
|
+
accumulator.previousNormalInternalAccounts.push(account);
|
446
|
+
}
|
447
|
+
return accumulator;
|
448
|
+
}, {
|
449
|
+
previousNormalInternalAccounts: [],
|
450
|
+
previousSnapInternalAccounts: [],
|
451
|
+
});
|
452
|
+
const addedAccounts = [];
|
453
|
+
const deletedAccounts = [];
|
454
|
+
// snap account ids are random uuid while normal accounts
|
455
|
+
// are determininistic based on the address
|
456
|
+
// ^NOTE: This will be removed when normal accounts also implement internal accounts
|
457
|
+
// finding all the normal accounts that were added
|
458
|
+
for (const account of updatedNormalKeyringAddresses) {
|
459
|
+
if (!this.state.internalAccounts.accounts[(0, utils_2.getUUIDFromAddressOfNormalAccount)(account.address)]) {
|
460
|
+
addedAccounts.push(account);
|
461
|
+
}
|
462
|
+
}
|
463
|
+
// finding all the snap accounts that were added
|
464
|
+
for (const account of updatedSnapKeyringAddresses) {
|
465
|
+
if (!previousSnapInternalAccounts.find((internalAccount) => internalAccount.address.toLowerCase() ===
|
466
|
+
account.address.toLowerCase())) {
|
467
|
+
addedAccounts.push(account);
|
468
|
+
}
|
469
|
+
}
|
470
|
+
// finding all the normal accounts that were deleted
|
471
|
+
for (const account of previousNormalInternalAccounts) {
|
472
|
+
if (!updatedNormalKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
|
473
|
+
deletedAccounts.push(account);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
// finding all the snap accounts that were deleted
|
477
|
+
for (const account of previousSnapInternalAccounts) {
|
478
|
+
if (!updatedSnapKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
|
479
|
+
deletedAccounts.push(account);
|
480
|
+
}
|
481
|
+
}
|
482
|
+
this.update((currentState) => {
|
483
|
+
if (deletedAccounts.length > 0) {
|
484
|
+
for (const account of deletedAccounts) {
|
485
|
+
currentState.internalAccounts.accounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleAccountRemoved).call(this, currentState.internalAccounts.accounts, account.id);
|
486
|
+
}
|
487
|
+
}
|
488
|
+
if (addedAccounts.length > 0) {
|
489
|
+
for (const account of addedAccounts) {
|
490
|
+
currentState.internalAccounts.accounts =
|
491
|
+
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleNewAccountAdded).call(this, currentState.internalAccounts.accounts, account);
|
492
|
+
}
|
493
|
+
}
|
494
|
+
// We don't use list accounts because it is not the updated state yet.
|
495
|
+
const existingAccounts = Object.values(currentState.internalAccounts.accounts);
|
496
|
+
// handle if the selected account was deleted
|
497
|
+
if (!currentState.internalAccounts.accounts[this.state.internalAccounts.selectedAccount]) {
|
498
|
+
const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, existingAccounts);
|
499
|
+
if (lastSelectedAccount) {
|
500
|
+
currentState.internalAccounts.selectedAccount =
|
501
|
+
lastSelectedAccount.id;
|
502
|
+
currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
|
503
|
+
__classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
|
504
|
+
}
|
505
|
+
else {
|
506
|
+
// It will be undefined if there are no accounts
|
507
|
+
currentState.internalAccounts.selectedAccount = '';
|
508
|
+
}
|
509
|
+
}
|
510
|
+
});
|
511
|
+
}
|
512
|
+
}, _AccountsController_handleOnSnapStateChange = function _AccountsController_handleOnSnapStateChange(snapState) {
|
513
|
+
// only check if snaps changed in status
|
514
|
+
const { snaps } = snapState;
|
515
|
+
const accounts = this.listMultichainAccounts().filter((account) => account.metadata.snap);
|
516
|
+
this.update((currentState) => {
|
517
|
+
accounts.forEach((account) => {
|
518
|
+
const currentAccount = currentState.internalAccounts.accounts[account.id];
|
519
|
+
if (currentAccount.metadata.snap) {
|
520
|
+
const snapId = currentAccount.metadata.snap.id;
|
521
|
+
const storedSnap = snaps[snapId];
|
522
|
+
if (storedSnap) {
|
523
|
+
currentAccount.metadata.snap.enabled =
|
524
|
+
storedSnap.enabled && !storedSnap.blocked;
|
525
|
+
}
|
526
|
+
}
|
527
|
+
});
|
528
|
+
});
|
529
|
+
}, _AccountsController_getAccountsByKeyringType = function _AccountsController_getAccountsByKeyringType(keyringType, accounts) {
|
530
|
+
return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
|
531
|
+
// We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
|
532
|
+
// to group those accounts together!
|
533
|
+
if (keyringType === keyring_controller_1.KeyringTypes.hd ||
|
534
|
+
keyringType === keyring_controller_1.KeyringTypes.simple) {
|
535
|
+
return (internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.hd ||
|
536
|
+
internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.simple);
|
537
|
+
}
|
538
|
+
return internalAccount.metadata.keyring.type === keyringType;
|
539
|
+
});
|
540
|
+
}, _AccountsController_getLastSelectedAccount = function _AccountsController_getLastSelectedAccount(accounts) {
|
541
|
+
const [accountToSelect] = accounts.sort((accountA, accountB) => {
|
542
|
+
// sort by lastSelected descending
|
543
|
+
return ((accountB.metadata.lastSelected ?? 0) -
|
544
|
+
(accountA.metadata.lastSelected ?? 0));
|
545
|
+
});
|
546
|
+
return accountToSelect;
|
547
|
+
}, _AccountsController_isAccountCompatibleWithChain = function _AccountsController_isAccountCompatibleWithChain(account, chainId) {
|
548
|
+
// TODO: Change this logic to not use account's type
|
549
|
+
// Because we currently only use type, we can only use namespace for now.
|
550
|
+
return account.type.startsWith((0, utils_1.parseCaipChainId)(chainId).namespace);
|
551
|
+
}, _AccountsController_getLastSelectedIndex = function _AccountsController_getLastSelectedIndex() {
|
552
|
+
// NOTE: For now we use the current date, since we know this value
|
553
|
+
// will always be higher than any already selected account index.
|
554
|
+
return Date.now();
|
555
|
+
}, _AccountsController_handleNewAccountAdded = function _AccountsController_handleNewAccountAdded(accountsState, account) {
|
556
|
+
let newAccount;
|
557
|
+
if (account.type !== keyring_controller_1.KeyringTypes.snap) {
|
558
|
+
newAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_generateInternalAccountForNonSnapAccount).call(this, account.address, account.type);
|
559
|
+
}
|
560
|
+
else {
|
561
|
+
const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
|
562
|
+
newAccount = snapKeyring.getAccountByAddress(account.address);
|
563
|
+
// The snap deleted the account before the keyring controller could add it
|
564
|
+
if (!newAccount) {
|
565
|
+
return accountsState;
|
566
|
+
}
|
567
|
+
}
|
568
|
+
const isFirstAccount = Object.keys(accountsState).length === 0;
|
569
|
+
// Get next account name available for this given keyring
|
570
|
+
const accountName = this.getNextAvailableAccountName(newAccount.metadata.keyring.type, Object.values(accountsState));
|
571
|
+
const newAccountWithUpdatedMetadata = {
|
572
|
+
...newAccount,
|
573
|
+
metadata: {
|
574
|
+
...newAccount.metadata,
|
575
|
+
name: accountName,
|
576
|
+
importTime: Date.now(),
|
577
|
+
lastSelected: isFirstAccount ? __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this) : 0,
|
578
|
+
},
|
579
|
+
};
|
580
|
+
accountsState[newAccount.id] = newAccountWithUpdatedMetadata;
|
581
|
+
this.messagingSystem.publish('AccountsController:accountAdded', newAccountWithUpdatedMetadata);
|
582
|
+
return accountsState;
|
583
|
+
}, _AccountsController_publishAccountChangeEvent = function _AccountsController_publishAccountChangeEvent(account) {
|
584
|
+
if ((0, keyring_api_1.isEvmAccountType)(account.type)) {
|
585
|
+
this.messagingSystem.publish('AccountsController:selectedEvmAccountChange', account);
|
586
|
+
}
|
587
|
+
this.messagingSystem.publish('AccountsController:selectedAccountChange', account);
|
588
|
+
}, _AccountsController_handleAccountRemoved = function _AccountsController_handleAccountRemoved(accountsState, accountId) {
|
589
|
+
delete accountsState[accountId];
|
590
|
+
this.messagingSystem.publish('AccountsController:accountRemoved', accountId);
|
591
|
+
return accountsState;
|
592
|
+
}, _AccountsController_populateExistingMetadata = function _AccountsController_populateExistingMetadata(accountId, metadataKey, account) {
|
593
|
+
const internalAccount = account ?? this.getAccount(accountId);
|
594
|
+
return internalAccount ? internalAccount.metadata[metadataKey] : undefined;
|
595
|
+
}, _AccountsController_registerMessageHandlers = function _AccountsController_registerMessageHandlers() {
|
596
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccount`, this.setSelectedAccount.bind(this));
|
597
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:listAccounts`, this.listAccounts.bind(this));
|
598
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:listMultichainAccounts`, this.listMultichainAccounts.bind(this));
|
599
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:setAccountName`, this.setAccountName.bind(this));
|
600
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:updateAccounts`, this.updateAccounts.bind(this));
|
601
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccount`, this.getSelectedAccount.bind(this));
|
602
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedMultichainAccount`, this.getSelectedMultichainAccount.bind(this));
|
603
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getAccountByAddress`, this.getAccountByAddress.bind(this));
|
604
|
+
this.messagingSystem.registerActionHandler(`${controllerName}:getNextAvailableAccountName`, this.getNextAvailableAccountName.bind(this));
|
605
|
+
this.messagingSystem.registerActionHandler(`AccountsController:getAccount`, this.getAccount.bind(this));
|
606
|
+
this.messagingSystem.registerActionHandler(`AccountsController:updateAccountMetadata`, this.updateAccountMetadata.bind(this));
|
607
|
+
};
|
608
|
+
//# sourceMappingURL=AccountsController.cjs.map
|