@metamask-previews/snap-account-service 0.2.0-preview-9fb8d00e7 → 0.2.0-preview-e43dfcb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +0 -17
- package/dist/SnapAccountService.cjs +13 -63
- package/dist/SnapAccountService.cjs.map +1 -1
- package/dist/SnapAccountService.d.cts +10 -3
- package/dist/SnapAccountService.d.cts.map +1 -1
- package/dist/SnapAccountService.d.mts +10 -3
- package/dist/SnapAccountService.d.mts.map +1 -1
- package/dist/SnapAccountService.mjs +14 -64
- package/dist/SnapAccountService.mjs.map +1 -1
- package/dist/SnapTracker.cjs +32 -10
- package/dist/SnapTracker.cjs.map +1 -1
- package/dist/SnapTracker.d.cts +6 -0
- package/dist/SnapTracker.d.cts.map +1 -1
- package/dist/SnapTracker.d.mts +6 -0
- package/dist/SnapTracker.d.mts.map +1 -1
- package/dist/SnapTracker.mjs +32 -10
- package/dist/SnapTracker.mjs.map +1 -1
- package/package.json +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,23 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Changed
|
|
11
|
-
|
|
12
|
-
- Faster `:getLegacySnapKeyring` ([#8865](https://github.com/MetaMask/core/pull/8865))
|
|
13
|
-
- We now check if the keyring exists with `:withKeyringUnsafe` and returns it right away.
|
|
14
|
-
- If the keyring does not exist yet, we do create it with `:withController` (next calls will then be faster thanks to `:withKeyringUnsafe` pre-check).
|
|
15
|
-
|
|
16
|
-
### Fixed
|
|
17
|
-
|
|
18
|
-
- Prevent double-lock in `:handleKeyringSnapMessage` for some events/methods ([#8860](https://github.com/MetaMask/core/pull/8860))
|
|
19
|
-
- The service messenger now requires the `KeyringController:withKeyringUnsafe` action.
|
|
20
|
-
- We now check if the keyring is available before delegating those messages.
|
|
21
|
-
- We still auto-create the keyring in some specific calls (e.g `notify:accountCreated`).
|
|
22
|
-
|
|
23
|
-
## Removed
|
|
24
|
-
|
|
25
|
-
- Removed `init` in favor of synchronous initialization when constructing the service ([#8877](https://github.com/MetaMask/core/pull/8877))
|
|
26
|
-
|
|
27
10
|
## [0.2.0]
|
|
28
11
|
|
|
29
12
|
### Added
|
|
@@ -10,13 +10,10 @@ 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 _SnapAccountService_instances, _SnapAccountService_messenger, _SnapAccountService_watcher, _SnapAccountService_tracker, _SnapAccountService_handleSelectedAccountGroupChange, _SnapAccountService_handleUnlock, _SnapAccountService_handleAccountGroupCreatedOrUpdated, _SnapAccountService_handleAccountGroupRemoved,
|
|
13
|
+
var _SnapAccountService_instances, _SnapAccountService_messenger, _SnapAccountService_watcher, _SnapAccountService_tracker, _SnapAccountService_handleSelectedAccountGroupChange, _SnapAccountService_handleUnlock, _SnapAccountService_handleAccountGroupCreatedOrUpdated, _SnapAccountService_handleAccountGroupRemoved, _SnapAccountService_forwardSelectedAccounts, _SnapAccountService_getAccountGroup, _SnapAccountService_getSelectedAccountGroupId;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.SnapAccountService = exports.serviceName = void 0;
|
|
16
|
-
const eth_snap_keyring_1 = require("@metamask/eth-snap-keyring");
|
|
17
|
-
const keyring_api_1 = require("@metamask/keyring-api");
|
|
18
16
|
const keyring_controller_1 = require("@metamask/keyring-controller");
|
|
19
|
-
const keyring_snap_sdk_1 = require("@metamask/keyring-snap-sdk");
|
|
20
17
|
const logger_1 = require("./logger.cjs");
|
|
21
18
|
const SnapPlatformWatcher_1 = require("./SnapPlatformWatcher.cjs");
|
|
22
19
|
const SnapTracker_1 = require("./SnapTracker.cjs");
|
|
@@ -72,6 +69,16 @@ class SnapAccountService {
|
|
|
72
69
|
__classPrivateFieldGet(this, _SnapAccountService_messenger, "f").subscribe('AccountTreeController:accountGroupRemoved', (groupId) => __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_handleAccountGroupRemoved).call(this, groupId));
|
|
73
70
|
__classPrivateFieldGet(this, _SnapAccountService_messenger, "f").subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_handleUnlock).call(this));
|
|
74
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Initializes the snap account service.
|
|
74
|
+
*
|
|
75
|
+
* Seeds the internal set of account-management Snaps from
|
|
76
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
77
|
+
* events.
|
|
78
|
+
*/
|
|
79
|
+
async init() {
|
|
80
|
+
await __classPrivateFieldGet(this, _SnapAccountService_tracker, "f").init();
|
|
81
|
+
}
|
|
75
82
|
/**
|
|
76
83
|
* Returns the IDs of all currently tracked account-management Snaps —
|
|
77
84
|
* Snaps that are installed, enabled, not blocked, and have the
|
|
@@ -107,14 +114,6 @@ class SnapAccountService {
|
|
|
107
114
|
* @returns The existing or newly-created Snap keyring instance.
|
|
108
115
|
*/
|
|
109
116
|
async getLegacySnapKeyring() {
|
|
110
|
-
// This is a fast-path for the common case where the keyring already exists, to avoid the
|
|
111
|
-
// overhead of acquiring the `KeyringController` mutex if we don't need to.
|
|
112
|
-
// NOTE: If it doesn't exist, we'll create it **safely** with `:withController` (which was
|
|
113
|
-
// not the case with the previous client's implementation).
|
|
114
|
-
const exists = await __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_getLegacySnapKeyringIfAvailable).call(this);
|
|
115
|
-
if (exists) {
|
|
116
|
-
return exists;
|
|
117
|
-
}
|
|
118
117
|
// `KeyringController:withController` forbids returning a direct keyring
|
|
119
118
|
// reference (it checks the result via `Object.is`), so we smuggle the
|
|
120
119
|
// instance out wrapped in an object and unwrap it after the call.
|
|
@@ -145,31 +144,7 @@ class SnapAccountService {
|
|
|
145
144
|
* @returns The execution result.
|
|
146
145
|
*/
|
|
147
146
|
async handleKeyringSnapMessage(snapId, message) {
|
|
148
|
-
|
|
149
|
-
// Handle specific methods first.
|
|
150
|
-
if (message.method === keyring_snap_sdk_1.SnapManageAccountsMethod.GetSelectedAccounts) {
|
|
151
|
-
if (snapKeyring) {
|
|
152
|
-
// The legacy Snap keyring already maintain a local list of selected accounts per Snaps, so we
|
|
153
|
-
// just delegate the call.
|
|
154
|
-
return snapKeyring.handleKeyringSnapMessage(snapId, message);
|
|
155
|
-
}
|
|
156
|
-
// Some Snaps might be using `getSelectedAccounts` early in their lifecycle, before the keyring is created. So we
|
|
157
|
-
// do not throw in that case to avoid messing up their lifecycle.
|
|
158
|
-
return [];
|
|
159
|
-
}
|
|
160
|
-
const event = message.method; // We assume the Snap platform always sends a valid `KeyringEvent` here.
|
|
161
|
-
(0, logger_1.projectLogger)(`Forwarding message "${event}" from Snap "${snapId}" to its keyring...`);
|
|
162
|
-
// We can create a new keyring if the message is an AccountCreated event.
|
|
163
|
-
const isAccountCreatedMessage = event === keyring_api_1.KeyringEvent.AccountCreated;
|
|
164
|
-
// Create the Snap keyring if it doesn't exist yet (in an atomic way). We cannot assume
|
|
165
|
-
// the keyring exists (e.g for the MMI Snap).
|
|
166
|
-
// NOTE: We only auto-create it for v1 account creation flows.
|
|
167
|
-
if (isAccountCreatedMessage && !snapKeyring) {
|
|
168
|
-
snapKeyring = await this.getLegacySnapKeyring();
|
|
169
|
-
}
|
|
170
|
-
if (!snapKeyring) {
|
|
171
|
-
throw new Error(`Legacy Snap keyring does not exist yet for snap "${snapId}".`);
|
|
172
|
-
}
|
|
147
|
+
const snapKeyring = await this.getLegacySnapKeyring();
|
|
173
148
|
return snapKeyring.handleKeyringSnapMessage(snapId, message);
|
|
174
149
|
}
|
|
175
150
|
}
|
|
@@ -187,27 +162,6 @@ _SnapAccountService_messenger = new WeakMap(), _SnapAccountService_watcher = new
|
|
|
187
162
|
if (groupId === __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_getSelectedAccountGroupId).call(this)) {
|
|
188
163
|
__classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_forwardSelectedAccounts).call(this, groupId, []);
|
|
189
164
|
}
|
|
190
|
-
}, _SnapAccountService_getLegacySnapKeyringIfAvailable =
|
|
191
|
-
/**
|
|
192
|
-
* Gets the legacy (v1) Snap keyring but do not auto-create it if it doesn't exist.
|
|
193
|
-
*
|
|
194
|
-
* @returns The existing Snap keyring instance, or undefined if it doesn't exist.
|
|
195
|
-
*/
|
|
196
|
-
async function _SnapAccountService_getLegacySnapKeyringIfAvailable() {
|
|
197
|
-
try {
|
|
198
|
-
const result = await __classPrivateFieldGet(this, _SnapAccountService_messenger, "f").call('KeyringController:withKeyringUnsafe', { filter: isLegacySnapKeyring }, async ({ keyring }) => {
|
|
199
|
-
// The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.
|
|
200
|
-
return { snapKeyring: keyring };
|
|
201
|
-
});
|
|
202
|
-
return result.snapKeyring;
|
|
203
|
-
}
|
|
204
|
-
catch (error) {
|
|
205
|
-
if ((0, keyring_controller_1.isKeyringNotFoundError)(error)) {
|
|
206
|
-
(0, logger_1.projectLogger)('Legacy Snap keyring not available yet.');
|
|
207
|
-
return undefined;
|
|
208
|
-
}
|
|
209
|
-
throw error;
|
|
210
|
-
}
|
|
211
165
|
}, _SnapAccountService_forwardSelectedAccounts = function _SnapAccountService_forwardSelectedAccounts(groupId, accounts) {
|
|
212
166
|
if (!groupId) {
|
|
213
167
|
(0, logger_1.projectLogger)('No selected account group, skipping forwarding selected accounts to Snap keyring.');
|
|
@@ -224,11 +178,7 @@ async function _SnapAccountService_getLegacySnapKeyringIfAvailable() {
|
|
|
224
178
|
else {
|
|
225
179
|
(0, logger_1.projectLogger)(`Clearing selected accounts (from "${groupId}")`);
|
|
226
180
|
}
|
|
227
|
-
const snapKeyring = await
|
|
228
|
-
if (!snapKeyring) {
|
|
229
|
-
(0, logger_1.projectLogger)('No legacy Snap keyring available, skipping forwarding selected accounts.');
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
181
|
+
const snapKeyring = await this.getLegacySnapKeyring();
|
|
232
182
|
await snapKeyring.setSelectedAccounts(accounts);
|
|
233
183
|
};
|
|
234
184
|
// There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapAccountService.cjs","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,iEAGoC;AACpC,uDAAqD;AAQrD,qEAGsC;AAEtC,iEAAsE;AAkBtE,yCAAgD;AAOhD,mEAA4D;AAE5D,mDAA4C;AAW5C;;;GAGG;AACU,QAAA,WAAW,GAAG,oBAAoB,CAAC;AAEhD;;;GAGG;AACH,MAAM,yBAAyB,GAAG;IAChC,aAAa;IACb,UAAU;IACV,sBAAsB;IACtB,0BAA0B;CAClB,CAAC;AAwEX;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,OAE5B;IACC,OAAO,OAAO,CAAC,IAAI,KAAK,iCAAY,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAa,kBAAkB;IAY7B;;;;;;OAMG;IACH,YAAY,EAAE,SAAS,EAAE,MAAM,EAA6B;;QAbnD,gDAAwC;QAExC,8CAA8B;QAE9B,8CAAsB;QAU7B,IAAI,CAAC,IAAI,GAAG,mBAAW,CAAC;QACxB,uBAAA,IAAI,iCAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,+BAAY,IAAI,yCAAmB,CACrC,SAAS,EACT,MAAM,EAAE,mBAAmB,CAC5B,MAAA,CAAC;QACF,uBAAA,IAAI,+BAAY,IAAI,yBAAW,CAAC,SAAS,CAAC,MAAA,CAAC;QAE3C,uBAAA,IAAI,qCAAW,CAAC,4BAA4B,CAC1C,IAAI,EACJ,yBAAyB,CAC1B,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,kDAAkD,EAClD,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,2FAAkC,MAAtC,IAAI,EAAmC,OAAO,CAAC,CAC7D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAA,IAAI,6FAAoC,MAAxC,IAAI,EAAqC,KAAK,CAAC,CAC3D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAA,IAAI,6FAAoC,MAAxC,IAAI,EAAqC,KAAK,CAAC,CAC3D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,EAA4B,OAAO,CAAC,CACtD,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CAAC,0BAA0B,EAAE,GAAG,EAAE,CACzD,uBAAA,IAAI,uEAAc,MAAlB,IAAI,CAAgB,CACrB,CAAC;IACJ,CAAC;IAsDD;;;;;;OAMG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,mCAAS,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,MAAc;QAC9B,IAAI,CAAC,uBAAA,IAAI,mCAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,uBAAA,IAAI,mCAAS,CAAC,wBAAwB,EAAE,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,oBAAoB;QAKxB,yFAAyF;QACzF,2EAA2E;QAC3E,0FAA0F;QAC1F,2DAA2D;QAC3D,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,0FAAiC,MAArC,IAAI,CAAmC,CAAC;QAC7D,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,wEAAwE;QACxE,sEAAsE;QACtE,kEAAkE;QAClE,sFAAsF;QACtF,mFAAmF;QACnF,2DAA2D;QAC3D,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACvC,kCAAkC,EAClC,KAAK,EAAE,UAAU,EAAmB,EAAE;YACpC,IAAI,WAAgD,CAAC;YAErD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CACrD,mBAAmB,CAAC,OAAO,CAAC,CAC7B,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;YAC9B,CAAC;YAED,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,EACJ,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,EAAE,EAAE,EAAE,GACjB,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,iCAAY,CAAC,IAAI,CAAC,CAAC;gBACtD,WAAW,GAAG,cAAc,CAAC;gBAE7B,IAAA,sBAAG,EAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC;YAChD,CAAC;YAED,wFAAwF;YACxF,OAAO,EAAE,WAAW,EAAuB,CAAC;QAC9C,CAAC,CACF,CAAC;QAEF,OAAQ,MAAiB,CAAC,WAAW,CAAC;IACxC,CAAC;IAmCD;;;;;;OAMG;IACH,KAAK,CAAC,wBAAwB,CAC5B,MAAc,EACd,OAAoB;QAEpB,IAAI,WAAW,GACb,MAAM,uBAAA,IAAI,0FAAiC,MAArC,IAAI,CAAmC,CAAC;QAEhD,iCAAiC;QACjC,IAAI,OAAO,CAAC,MAAM,KAAK,2CAAwB,CAAC,mBAAmB,EAAE,CAAC;YACpE,IAAI,WAAW,EAAE,CAAC;gBAChB,8FAA8F;gBAC9F,0BAA0B;gBAC1B,OAAO,WAAW,CAAC,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC/D,CAAC;YAED,iHAAiH;YACjH,iEAAiE;YACjE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,MAAsB,CAAC,CAAC,wEAAwE;QACtH,IAAA,sBAAG,EACD,uBAAuB,KAAK,gBAAgB,MAAM,qBAAqB,CACxE,CAAC;QAEF,yEAAyE;QACzE,MAAM,uBAAuB,GAAG,KAAK,KAAK,0BAAY,CAAC,cAAc,CAAC;QAEtE,uFAAuF;QACvF,6CAA6C;QAC7C,8DAA8D;QAC9D,IAAI,uBAAuB,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5C,WAAW,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAClD,CAAC;QAED,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,oDAAoD,MAAM,IAAI,CAC/D,CAAC;QACJ,CAAC;QAED,OAAO,WAAW,CAAC,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;CAmFF;AAzWD,gDAyWC;6SAzSmC,OAA4B;IAC5D,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,uBAAA,IAAI,0EAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,EAAE,QAAQ,CACzC,CAAC;AACJ,CAAC;IAOC,MAAM,OAAO,GAAG,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,CAAC;IAClD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,uBAAA,IAAI,0EAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,EAAE,QAAQ,CACzC,CAAC;AACJ,CAAC,2HAQmC,KAAyB;IAC3D,IAAI,KAAK,CAAC,EAAE,KAAK,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,EAAE,CAAC;QACnD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EAA0B,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC,yGAQ0B,OAAuB;IAChD,IAAI,OAAO,KAAK,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,EAAE,CAAC;QAClD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC;AAwFD;;;;GAIG;AACH,KAAK;IAOH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACvC,qCAAqC,EACrC,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,EAAmB,EAAE;YACrC,wFAAwF;YACxF,OAAO,EAAE,WAAW,EAAE,OAAO,EAAuB,CAAC;QACvD,CAAC,CACF,CAAC;QAEF,OAAQ,MAAiB,CAAC,WAAW,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,IAAA,2CAAsB,EAAC,KAAK,CAAC,EAAE,CAAC;YAClC,IAAA,sBAAG,EAAC,wCAAwC,CAAC,CAAC;YAC9C,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,qGA6DC,OAA4B,EAC5B,QAAiC;IAEjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAA,sBAAG,EACD,mFAAmF,CACpF,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAA,sBAAG,EACD,mBAAmB,OAAO,4EAA4E,CACvG,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,uBAAuB,GAAG,KAAK,IAAmB,EAAE;QACxD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACpB,IAAA,sBAAG,EACD,uCAAuC,OAAO,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAA,sBAAG,EAAC,qCAAqC,OAAO,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,0FAAiC,MAArC,IAAI,CAAmC,CAAC;QAClE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,IAAA,sBAAG,EACD,0EAA0E,CAC3E,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,WAAW,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC;IAEF,qGAAqG;IACrG,uBAAuB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,qFASC,OAA4B;IAE5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACzB,6CAA6C,EAC7C,OAAO,CACR,CAAC;AACJ,CAAC;IASC,OAAO,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACzB,+CAA+C,CAChD,CAAC;AACJ,CAAC","sourcesContent":["import { AccountGroupId } from '@metamask/account-api';\nimport {\n SnapKeyring as LegacySnapKeyring,\n SnapMessage,\n} from '@metamask/eth-snap-keyring';\nimport { KeyringEvent } from '@metamask/keyring-api';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithControllerAction,\n KeyringEntry,\n} from '@metamask/keyring-controller';\nimport {\n isKeyringNotFoundError,\n KeyringTypes,\n} from '@metamask/keyring-controller';\nimport { KeyringControllerWithKeyringUnsafeAction } from '@metamask/keyring-controller';\nimport { SnapManageAccountsMethod } from '@metamask/keyring-snap-sdk';\nimport type { AccountId, BaseKeyring } from '@metamask/keyring-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerGetSnapAction,\n SnapControllerGetStateAction,\n SnapControllerSnapBlockedEvent,\n SnapControllerSnapDisabledEvent,\n SnapControllerSnapEnabledEvent,\n SnapControllerSnapInstalledEvent,\n SnapControllerSnapUnblockedEvent,\n SnapControllerSnapUninstalledEvent,\n SnapControllerStateChangeEvent,\n} from '@metamask/snaps-controllers';\nimport { SnapId } from '@metamask/snaps-sdk';\nimport type { Json } from '@metamask/utils';\n\nimport { projectLogger as log } from './logger';\nimport type {\n SnapAccountServiceEnsureReadyAction,\n SnapAccountServiceGetLegacySnapKeyringAction,\n SnapAccountServiceGetSnapsAction,\n SnapAccountServiceHandleKeyringSnapMessageAction,\n} from './SnapAccountService-method-action-types';\nimport { SnapPlatformWatcher } from './SnapPlatformWatcher';\nimport type { SnapPlatformWatcherConfig } from './SnapPlatformWatcher';\nimport { SnapTracker } from './SnapTracker';\nimport type {\n AccountTreeControllerGetAccountGroupObjectAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\n AccountTreeControllerSelectedAccountGroupChangeEvent,\n AccountTreeControllerAccountGroupCreatedEvent,\n AccountTreeControllerAccountGroupUpdatedEvent,\n AccountTreeControllerAccountGroupRemovedEvent,\n AccountGroupObject,\n} from './types';\n\n/**\n * The name of the {@link SnapAccountService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'SnapAccountService';\n\n/**\n * All of the methods within {@link SnapAccountService} that are exposed via\n * the messenger.\n */\nconst MESSENGER_EXPOSED_METHODS = [\n 'ensureReady',\n 'getSnaps',\n 'getLegacySnapKeyring',\n 'handleKeyringSnapMessage',\n] as const;\n\n/**\n * Actions that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceActions =\n | SnapAccountServiceEnsureReadyAction\n | SnapAccountServiceGetSnapsAction\n | SnapAccountServiceGetLegacySnapKeyringAction\n | SnapAccountServiceHandleKeyringSnapMessageAction;\n\n/**\n * Actions from other messengers that {@link SnapAccountService} calls.\n */\ntype AllowedActions =\n | SnapControllerGetStateAction\n | SnapControllerGetSnapAction\n | SnapControllerGetRunnableSnapsAction\n | KeyringControllerGetStateAction\n | KeyringControllerWithControllerAction\n | KeyringControllerWithKeyringUnsafeAction\n | AccountTreeControllerGetAccountGroupObjectAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\n/**\n * Events that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceEvents = never;\n\n/**\n * Events from other messengers that {@link SnapAccountService} subscribes to.\n */\ntype AllowedEvents =\n | SnapControllerStateChangeEvent\n | SnapControllerSnapInstalledEvent\n | SnapControllerSnapEnabledEvent\n | SnapControllerSnapDisabledEvent\n | SnapControllerSnapBlockedEvent\n | SnapControllerSnapUnblockedEvent\n | SnapControllerSnapUninstalledEvent\n | KeyringControllerStateChangeEvent\n | KeyringControllerUnlockEvent\n | AccountTreeControllerSelectedAccountGroupChangeEvent\n | AccountTreeControllerAccountGroupCreatedEvent\n | AccountTreeControllerAccountGroupUpdatedEvent\n | AccountTreeControllerAccountGroupRemovedEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SnapAccountService}.\n */\nexport type SnapAccountServiceMessenger = Messenger<\n typeof serviceName,\n SnapAccountServiceActions | AllowedActions,\n SnapAccountServiceEvents | AllowedEvents\n>;\n\n/**\n * Configuration for the {@link SnapAccountService}.\n */\nexport type SnapAccountServiceConfig = {\n snapPlatformWatcher?: SnapPlatformWatcherConfig;\n};\n\n/**\n * The options that {@link SnapAccountService} takes.\n */\nexport type SnapAccountServiceOptions = {\n messenger: SnapAccountServiceMessenger;\n config?: SnapAccountServiceConfig;\n};\n\n/**\n * Checks if a given keyring is a Snap keyring (v2).\n *\n * @param keyring - The keyring to check.\n * @param keyring.type - The type of the keyring.\n * @returns `true` if the keyring is a Snap keyring (v2), `false` otherwise.\n */\nfunction isLegacySnapKeyring(keyring: {\n type: BaseKeyring['type'];\n}): keyring is LegacySnapKeyring {\n return keyring.type === KeyringTypes.snap;\n}\n\n/**\n * Service responsible for managing account management snaps.\n */\nexport class SnapAccountService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #watcher: SnapPlatformWatcher;\n\n readonly #tracker: SnapTracker;\n\n /**\n * Constructs a new {@link SnapAccountService}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.config - Optional service configuration.\n */\n constructor({ messenger, config }: SnapAccountServiceOptions) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#watcher = new SnapPlatformWatcher(\n messenger,\n config?.snapPlatformWatcher,\n );\n this.#tracker = new SnapTracker(messenger);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:selectedAccountGroupChange',\n (groupId) => this.#handleSelectedAccountGroupChange(groupId),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupCreated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupUpdated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupRemoved',\n (groupId) => this.#handleAccountGroupRemoved(groupId),\n );\n\n this.#messenger.subscribe('KeyringController:unlock', () =>\n this.#handleUnlock(),\n );\n }\n\n /**\n * Handles changes to the selected account group by forwarding the new\n * group's accounts to the Snap keyring.\n *\n * @param groupId - The ID of the newly selected account group.\n */\n #handleSelectedAccountGroupChange(groupId: AccountGroupId | ''): void {\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles the keyring controller unlock event by forwarding the currently\n * selected account group's accounts to the Snap keyring.\n */\n #handleUnlock(): void {\n const groupId = this.#getSelectedAccountGroupId();\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles created or updated account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the created/updated group is currently selected.\n *\n * @param group - The account group being created or updated.\n */\n #handleAccountGroupCreatedOrUpdated(group: AccountGroupObject): void {\n if (group.id === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(group.id, group.accounts);\n }\n }\n\n /**\n * Handles removed account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the removed group is currently selected.\n *\n * @param groupId - The ID of the account group being removed.\n */\n #handleAccountGroupRemoved(groupId: AccountGroupId): void {\n if (groupId === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(\n groupId,\n [], // Clearing accounts since the group is removed\n );\n }\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps —\n * Snaps that are installed, enabled, not blocked, and have the\n * `endowment:keyring` permission.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return this.#tracker.getSnaps();\n }\n\n /**\n * Ensures everything is ready to use Snap accounts for the given Snap.\n * 1. Validates that `snapId` is a tracked account-management Snap.\n * 2. Waits for the Snap platform to be fully started.\n *\n * Safe to call concurrently — each step is idempotent or mutex-protected.\n *\n * @param snapId - ID of the Snap to ensure readiness for.\n * @throws If `snapId` is not a tracked account-management Snap.\n */\n async ensureReady(snapId: SnapId): Promise<void> {\n if (!this.#tracker.canUse(snapId)) {\n throw new Error(`Unknown snap: \"${snapId}\"`);\n }\n // Before doing anything with our Snap, we need to make sure the platform\n // is ready to process requests.\n await this.#watcher.ensureCanUseSnapPlatform();\n }\n\n /**\n * Atomically gets-or-creates the legacy (v1) Snap keyring — the keyring\n * associated with {@link KeyringTypes.snap}.\n *\n * @returns The existing or newly-created Snap keyring instance.\n */\n async getLegacySnapKeyring(): Promise<LegacySnapKeyring> {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n // This is a fast-path for the common case where the keyring already exists, to avoid the\n // overhead of acquiring the `KeyringController` mutex if we don't need to.\n // NOTE: If it doesn't exist, we'll create it **safely** with `:withController` (which was\n // not the case with the previous client's implementation).\n const exists = await this.#getLegacySnapKeyringIfAvailable();\n if (exists) {\n return exists;\n }\n\n // `KeyringController:withController` forbids returning a direct keyring\n // reference (it checks the result via `Object.is`), so we smuggle the\n // instance out wrapped in an object and unwrap it after the call.\n // NOTE: This violates the abstraction of `KeyringController:withController`, but this\n // is how we currently interact with the legacy Snap keyring. Once we migrate it to\n // the Snap keyring v2, we won't be using the same pattern.\n const result = await this.#messenger.call(\n 'KeyringController:withController',\n async (controller): Promise<Result> => {\n let snapKeyring: KeyringEntry['keyring'] | undefined;\n\n const found = controller.keyrings.find(({ keyring }) =>\n isLegacySnapKeyring(keyring),\n );\n if (found) {\n snapKeyring = found.keyring;\n }\n\n if (!snapKeyring) {\n const {\n keyring: newSnapKeyring,\n metadata: { id },\n } = await controller.addNewKeyring(KeyringTypes.snap);\n snapKeyring = newSnapKeyring;\n\n log(`Legacy Snap keyring created. (\"${id}\")`);\n }\n\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n }\n\n /**\n * Gets the legacy (v1) Snap keyring but do not auto-create it if it doesn't exist.\n *\n * @returns The existing Snap keyring instance, or undefined if it doesn't exist.\n */\n async #getLegacySnapKeyringIfAvailable(): Promise<\n LegacySnapKeyring | undefined\n > {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n try {\n const result = await this.#messenger.call(\n 'KeyringController:withKeyringUnsafe',\n { filter: isLegacySnapKeyring },\n async ({ keyring }): Promise<Result> => {\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring: keyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n } catch (error) {\n if (isKeyringNotFoundError(error)) {\n log('Legacy Snap keyring not available yet.');\n return undefined;\n }\n\n throw error;\n }\n }\n\n /**\n * Handle a message from a Snap.\n *\n * @param snapId - ID of the Snap.\n * @param message - Message sent by the Snap.\n * @returns The execution result.\n */\n async handleKeyringSnapMessage(\n snapId: SnapId,\n message: SnapMessage,\n ): Promise<Json> {\n let snapKeyring: LegacySnapKeyring | undefined =\n await this.#getLegacySnapKeyringIfAvailable();\n\n // Handle specific methods first.\n if (message.method === SnapManageAccountsMethod.GetSelectedAccounts) {\n if (snapKeyring) {\n // The legacy Snap keyring already maintain a local list of selected accounts per Snaps, so we\n // just delegate the call.\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n // Some Snaps might be using `getSelectedAccounts` early in their lifecycle, before the keyring is created. So we\n // do not throw in that case to avoid messing up their lifecycle.\n return [];\n }\n\n const event = message.method as KeyringEvent; // We assume the Snap platform always sends a valid `KeyringEvent` here.\n log(\n `Forwarding message \"${event}\" from Snap \"${snapId}\" to its keyring...`,\n );\n\n // We can create a new keyring if the message is an AccountCreated event.\n const isAccountCreatedMessage = event === KeyringEvent.AccountCreated;\n\n // Create the Snap keyring if it doesn't exist yet (in an atomic way). We cannot assume\n // the keyring exists (e.g for the MMI Snap).\n // NOTE: We only auto-create it for v1 account creation flows.\n if (isAccountCreatedMessage && !snapKeyring) {\n snapKeyring = await this.getLegacySnapKeyring();\n }\n\n if (!snapKeyring) {\n throw new Error(\n `Legacy Snap keyring does not exist yet for snap \"${snapId}\".`,\n );\n }\n\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n /**\n * Forwards the accounts of the given account group to the Snap keyring.\n *\n * @param groupId - The ID of the account group whose accounts should be\n * forwarded. If empty, this is a no-op.\n * @param accounts - The accounts to forward. If not defined, this is a no-op.\n */\n #forwardSelectedAccounts(\n groupId: AccountGroupId | '',\n accounts: AccountId[] | undefined,\n ): void {\n if (!groupId) {\n log(\n 'No selected account group, skipping forwarding selected accounts to Snap keyring.',\n );\n return;\n }\n\n if (!accounts) {\n log(\n `Account group (\"${groupId}\") has no accounts, skipping forwarding selected accounts to Snap keyring.`,\n );\n return;\n }\n\n const forwardSelectedAccounts = async (): Promise<void> => {\n if (accounts.length) {\n log(\n `Forwarding selected accounts (from \"${groupId}\"): ${accounts.join(', ')}`,\n );\n } else {\n log(`Clearing selected accounts (from \"${groupId}\")`);\n }\n\n const snapKeyring = await this.#getLegacySnapKeyringIfAvailable();\n if (!snapKeyring) {\n log(\n 'No legacy Snap keyring available, skipping forwarding selected accounts.',\n );\n return;\n }\n\n await snapKeyring.setSelectedAccounts(accounts);\n };\n\n // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.\n forwardSelectedAccounts().catch((error) => {\n console.error('Error forwarding selected accounts:', error);\n });\n }\n\n /**\n * Gets the account group object for the given group ID.\n *\n * @param groupId - The ID of the account group.\n * @returns The account group object, or undefined if the group ID is empty or the group does not exist.\n */\n #getAccountGroup(\n groupId: AccountGroupId | '',\n ): AccountGroupObject | undefined {\n if (!groupId) {\n return undefined;\n }\n\n return this.#messenger.call(\n 'AccountTreeController:getAccountGroupObject',\n groupId,\n );\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The currently selected account group ID, or an empty string if\n * there is no selected account group.\n */\n #getSelectedAccountGroupId(): AccountGroupId | '' {\n return this.#messenger.call(\n 'AccountTreeController:getSelectedAccountGroup',\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SnapAccountService.cjs","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountGroupId } from '@metamask/account-api';\nimport type {\n SnapKeyring as LegacySnapKeyring,\n SnapMessage,\n} from '@metamask/eth-snap-keyring';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithControllerAction,\n KeyringEntry,\n} from '@metamask/keyring-controller';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { AccountId, BaseKeyring } from '@metamask/keyring-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerGetSnapAction,\n SnapControllerGetStateAction,\n SnapControllerSnapBlockedEvent,\n SnapControllerSnapDisabledEvent,\n SnapControllerSnapEnabledEvent,\n SnapControllerSnapInstalledEvent,\n SnapControllerSnapUnblockedEvent,\n SnapControllerSnapUninstalledEvent,\n SnapControllerStateChangeEvent,\n} from '@metamask/snaps-controllers';\nimport { SnapId } from '@metamask/snaps-sdk';\nimport type { Json } from '@metamask/utils';\n\nimport { projectLogger as log } from './logger';\nimport type {\n SnapAccountServiceEnsureReadyAction,\n SnapAccountServiceGetLegacySnapKeyringAction,\n SnapAccountServiceGetSnapsAction,\n SnapAccountServiceHandleKeyringSnapMessageAction,\n} from './SnapAccountService-method-action-types';\nimport { SnapPlatformWatcher } from './SnapPlatformWatcher';\nimport type { SnapPlatformWatcherConfig } from './SnapPlatformWatcher';\nimport { SnapTracker } from './SnapTracker';\nimport type {\n AccountTreeControllerGetAccountGroupObjectAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\n AccountTreeControllerSelectedAccountGroupChangeEvent,\n AccountTreeControllerAccountGroupCreatedEvent,\n AccountTreeControllerAccountGroupUpdatedEvent,\n AccountTreeControllerAccountGroupRemovedEvent,\n AccountGroupObject,\n} from './types';\n\n/**\n * The name of the {@link SnapAccountService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'SnapAccountService';\n\n/**\n * All of the methods within {@link SnapAccountService} that are exposed via\n * the messenger.\n */\nconst MESSENGER_EXPOSED_METHODS = [\n 'ensureReady',\n 'getSnaps',\n 'getLegacySnapKeyring',\n 'handleKeyringSnapMessage',\n] as const;\n\n/**\n * Actions that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceActions =\n | SnapAccountServiceEnsureReadyAction\n | SnapAccountServiceGetSnapsAction\n | SnapAccountServiceGetLegacySnapKeyringAction\n | SnapAccountServiceHandleKeyringSnapMessageAction;\n\n/**\n * Actions from other messengers that {@link SnapAccountService} calls.\n */\ntype AllowedActions =\n | SnapControllerGetStateAction\n | SnapControllerGetSnapAction\n | SnapControllerGetRunnableSnapsAction\n | KeyringControllerGetStateAction\n | KeyringControllerWithControllerAction\n | AccountTreeControllerGetAccountGroupObjectAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\n/**\n * Events that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceEvents = never;\n\n/**\n * Events from other messengers that {@link SnapAccountService} subscribes to.\n */\ntype AllowedEvents =\n | SnapControllerStateChangeEvent\n | SnapControllerSnapInstalledEvent\n | SnapControllerSnapEnabledEvent\n | SnapControllerSnapDisabledEvent\n | SnapControllerSnapBlockedEvent\n | SnapControllerSnapUnblockedEvent\n | SnapControllerSnapUninstalledEvent\n | KeyringControllerStateChangeEvent\n | KeyringControllerUnlockEvent\n | AccountTreeControllerSelectedAccountGroupChangeEvent\n | AccountTreeControllerAccountGroupCreatedEvent\n | AccountTreeControllerAccountGroupUpdatedEvent\n | AccountTreeControllerAccountGroupRemovedEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SnapAccountService}.\n */\nexport type SnapAccountServiceMessenger = Messenger<\n typeof serviceName,\n SnapAccountServiceActions | AllowedActions,\n SnapAccountServiceEvents | AllowedEvents\n>;\n\n/**\n * Configuration for the {@link SnapAccountService}.\n */\nexport type SnapAccountServiceConfig = {\n snapPlatformWatcher?: SnapPlatformWatcherConfig;\n};\n\n/**\n * The options that {@link SnapAccountService} takes.\n */\nexport type SnapAccountServiceOptions = {\n messenger: SnapAccountServiceMessenger;\n config?: SnapAccountServiceConfig;\n};\n\n/**\n * Checks if a given keyring is a Snap keyring (v2).\n *\n * @param keyring - The keyring to check.\n * @param keyring.type - The type of the keyring.\n * @returns `true` if the keyring is a Snap keyring (v2), `false` otherwise.\n */\nfunction isLegacySnapKeyring(keyring: {\n type: BaseKeyring['type'];\n}): keyring is LegacySnapKeyring {\n return keyring.type === KeyringTypes.snap;\n}\n\n/**\n * Service responsible for managing account management snaps.\n */\nexport class SnapAccountService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #watcher: SnapPlatformWatcher;\n\n readonly #tracker: SnapTracker;\n\n /**\n * Constructs a new {@link SnapAccountService}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.config - Optional service configuration.\n */\n constructor({ messenger, config }: SnapAccountServiceOptions) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#watcher = new SnapPlatformWatcher(\n messenger,\n config?.snapPlatformWatcher,\n );\n this.#tracker = new SnapTracker(messenger);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:selectedAccountGroupChange',\n (groupId) => this.#handleSelectedAccountGroupChange(groupId),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupCreated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupUpdated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupRemoved',\n (groupId) => this.#handleAccountGroupRemoved(groupId),\n );\n\n this.#messenger.subscribe('KeyringController:unlock', () =>\n this.#handleUnlock(),\n );\n }\n\n /**\n * Handles changes to the selected account group by forwarding the new\n * group's accounts to the Snap keyring.\n *\n * @param groupId - The ID of the newly selected account group.\n */\n #handleSelectedAccountGroupChange(groupId: AccountGroupId | ''): void {\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles the keyring controller unlock event by forwarding the currently\n * selected account group's accounts to the Snap keyring.\n */\n #handleUnlock(): void {\n const groupId = this.#getSelectedAccountGroupId();\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles created or updated account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the created/updated group is currently selected.\n *\n * @param group - The account group being created or updated.\n */\n #handleAccountGroupCreatedOrUpdated(group: AccountGroupObject): void {\n if (group.id === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(group.id, group.accounts);\n }\n }\n\n /**\n * Handles removed account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the removed group is currently selected.\n *\n * @param groupId - The ID of the account group being removed.\n */\n #handleAccountGroupRemoved(groupId: AccountGroupId): void {\n if (groupId === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(\n groupId,\n [], // Clearing accounts since the group is removed\n );\n }\n }\n\n /**\n * Initializes the snap account service.\n *\n * Seeds the internal set of account-management Snaps from\n * `SnapController:getRunnableSnaps`, then starts processing lifecycle\n * events.\n */\n async init(): Promise<void> {\n await this.#tracker.init();\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps —\n * Snaps that are installed, enabled, not blocked, and have the\n * `endowment:keyring` permission.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return this.#tracker.getSnaps();\n }\n\n /**\n * Ensures everything is ready to use Snap accounts for the given Snap.\n * 1. Validates that `snapId` is a tracked account-management Snap.\n * 2. Waits for the Snap platform to be fully started.\n *\n * Safe to call concurrently — each step is idempotent or mutex-protected.\n *\n * @param snapId - ID of the Snap to ensure readiness for.\n * @throws If `snapId` is not a tracked account-management Snap.\n */\n async ensureReady(snapId: SnapId): Promise<void> {\n if (!this.#tracker.canUse(snapId)) {\n throw new Error(`Unknown snap: \"${snapId}\"`);\n }\n // Before doing anything with our Snap, we need to make sure the platform\n // is ready to process requests.\n await this.#watcher.ensureCanUseSnapPlatform();\n }\n\n /**\n * Atomically gets-or-creates the legacy (v1) Snap keyring — the keyring\n * associated with {@link KeyringTypes.snap}.\n *\n * @returns The existing or newly-created Snap keyring instance.\n */\n async getLegacySnapKeyring(): Promise<LegacySnapKeyring> {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n // `KeyringController:withController` forbids returning a direct keyring\n // reference (it checks the result via `Object.is`), so we smuggle the\n // instance out wrapped in an object and unwrap it after the call.\n // NOTE: This violates the abstraction of `KeyringController:withController`, but this\n // is how we currently interact with the legacy Snap keyring. Once we migrate it to\n // the Snap keyring v2, we won't be using the same pattern.\n const result = await this.#messenger.call(\n 'KeyringController:withController',\n async (controller): Promise<Result> => {\n let snapKeyring: KeyringEntry['keyring'] | undefined;\n\n const found = controller.keyrings.find(({ keyring }) =>\n isLegacySnapKeyring(keyring),\n );\n if (found) {\n snapKeyring = found.keyring;\n }\n\n if (!snapKeyring) {\n const {\n keyring: newSnapKeyring,\n metadata: { id },\n } = await controller.addNewKeyring(KeyringTypes.snap);\n snapKeyring = newSnapKeyring;\n\n log(`Legacy Snap keyring created. (\"${id}\")`);\n }\n\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n }\n\n /**\n * Handle a message from a Snap.\n *\n * @param snapId - ID of the Snap.\n * @param message - Message sent by the Snap.\n * @returns The execution result.\n */\n async handleKeyringSnapMessage(\n snapId: SnapId,\n message: SnapMessage,\n ): Promise<Json> {\n const snapKeyring = await this.getLegacySnapKeyring();\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n /**\n * Forwards the accounts of the given account group to the Snap keyring.\n *\n * @param groupId - The ID of the account group whose accounts should be\n * forwarded. If empty, this is a no-op.\n * @param accounts - The accounts to forward. If not defined, this is a no-op.\n */\n #forwardSelectedAccounts(\n groupId: AccountGroupId | '',\n accounts: AccountId[] | undefined,\n ): void {\n if (!groupId) {\n log(\n 'No selected account group, skipping forwarding selected accounts to Snap keyring.',\n );\n return;\n }\n\n if (!accounts) {\n log(\n `Account group (\"${groupId}\") has no accounts, skipping forwarding selected accounts to Snap keyring.`,\n );\n return;\n }\n\n const forwardSelectedAccounts = async (): Promise<void> => {\n if (accounts.length) {\n log(\n `Forwarding selected accounts (from \"${groupId}\"): ${accounts.join(', ')}`,\n );\n } else {\n log(`Clearing selected accounts (from \"${groupId}\")`);\n }\n\n const snapKeyring = await this.getLegacySnapKeyring();\n await snapKeyring.setSelectedAccounts(accounts);\n };\n\n // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.\n forwardSelectedAccounts().catch((error) => {\n console.error('Error forwarding selected accounts:', error);\n });\n }\n\n /**\n * Gets the account group object for the given group ID.\n *\n * @param groupId - The ID of the account group.\n * @returns The account group object, or undefined if the group ID is empty or the group does not exist.\n */\n #getAccountGroup(\n groupId: AccountGroupId | '',\n ): AccountGroupObject | undefined {\n if (!groupId) {\n return undefined;\n }\n\n return this.#messenger.call(\n 'AccountTreeController:getAccountGroupObject',\n groupId,\n );\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The currently selected account group ID, or an empty string if\n * there is no selected account group.\n */\n #getSelectedAccountGroupId(): AccountGroupId | '' {\n return this.#messenger.call(\n 'AccountTreeController:getSelectedAccountGroup',\n );\n }\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { SnapKeyring as LegacySnapKeyring, SnapMessage } from "@metamask/eth-snap-keyring";
|
|
1
|
+
import type { SnapKeyring as LegacySnapKeyring, SnapMessage } from "@metamask/eth-snap-keyring";
|
|
2
2
|
import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent, KeyringControllerUnlockEvent, KeyringControllerWithControllerAction } from "@metamask/keyring-controller";
|
|
3
|
-
import { KeyringControllerWithKeyringUnsafeAction } from "@metamask/keyring-controller";
|
|
4
3
|
import type { Messenger } from "@metamask/messenger";
|
|
5
4
|
import type { SnapControllerGetRunnableSnapsAction, SnapControllerGetSnapAction, SnapControllerGetStateAction, SnapControllerSnapBlockedEvent, SnapControllerSnapDisabledEvent, SnapControllerSnapEnabledEvent, SnapControllerSnapInstalledEvent, SnapControllerSnapUnblockedEvent, SnapControllerSnapUninstalledEvent, SnapControllerStateChangeEvent } from "@metamask/snaps-controllers";
|
|
6
5
|
import { SnapId } from "@metamask/snaps-sdk";
|
|
@@ -20,7 +19,7 @@ export type SnapAccountServiceActions = SnapAccountServiceEnsureReadyAction | Sn
|
|
|
20
19
|
/**
|
|
21
20
|
* Actions from other messengers that {@link SnapAccountService} calls.
|
|
22
21
|
*/
|
|
23
|
-
type AllowedActions = SnapControllerGetStateAction | SnapControllerGetSnapAction | SnapControllerGetRunnableSnapsAction | KeyringControllerGetStateAction | KeyringControllerWithControllerAction |
|
|
22
|
+
type AllowedActions = SnapControllerGetStateAction | SnapControllerGetSnapAction | SnapControllerGetRunnableSnapsAction | KeyringControllerGetStateAction | KeyringControllerWithControllerAction | AccountTreeControllerGetAccountGroupObjectAction | AccountTreeControllerGetSelectedAccountGroupAction;
|
|
24
23
|
/**
|
|
25
24
|
* Events that {@link SnapAccountService} exposes to other consumers.
|
|
26
25
|
*/
|
|
@@ -64,6 +63,14 @@ export declare class SnapAccountService {
|
|
|
64
63
|
* @param args.config - Optional service configuration.
|
|
65
64
|
*/
|
|
66
65
|
constructor({ messenger, config }: SnapAccountServiceOptions);
|
|
66
|
+
/**
|
|
67
|
+
* Initializes the snap account service.
|
|
68
|
+
*
|
|
69
|
+
* Seeds the internal set of account-management Snaps from
|
|
70
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
71
|
+
* events.
|
|
72
|
+
*/
|
|
73
|
+
init(): Promise<void>;
|
|
67
74
|
/**
|
|
68
75
|
* Returns the IDs of all currently tracked account-management Snaps —
|
|
69
76
|
* Snaps that are installed, enabled, not blocked, and have the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapAccountService.d.cts","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"SnapAccountService.d.cts","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,IAAI,iBAAiB,EAChC,WAAW,EACZ,mCAAmC;AACpC,OAAO,KAAK,EACV,+BAA+B,EAC/B,iCAAiC,EACjC,4BAA4B,EAC5B,qCAAqC,EAEtC,qCAAqC;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,oCAAoC,EACpC,2BAA2B,EAC3B,4BAA4B,EAC5B,8BAA8B,EAC9B,+BAA+B,EAC/B,8BAA8B,EAC9B,gCAAgC,EAChC,gCAAgC,EAChC,kCAAkC,EAClC,8BAA8B,EAC/B,oCAAoC;AACrC,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,OAAO,KAAK,EACV,mCAAmC,EACnC,4CAA4C,EAC5C,gCAAgC,EAChC,gDAAgD,EACjD,qDAAiD;AAElD,OAAO,KAAK,EAAE,yBAAyB,EAAE,kCAA8B;AAEvE,OAAO,KAAK,EACV,gDAAgD,EAChD,kDAAkD,EAClD,oDAAoD,EACpD,6CAA6C,EAC7C,6CAA6C,EAC7C,6CAA6C,EAE9C,oBAAgB;AAEjB;;;GAGG;AACH,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAahD;;GAEG;AACH,MAAM,MAAM,yBAAyB,GACjC,mCAAmC,GACnC,gCAAgC,GAChC,4CAA4C,GAC5C,gDAAgD,CAAC;AAErD;;GAEG;AACH,KAAK,cAAc,GACf,4BAA4B,GAC5B,2BAA2B,GAC3B,oCAAoC,GACpC,+BAA+B,GAC/B,qCAAqC,GACrC,gDAAgD,GAChD,kDAAkD,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE7C;;GAEG;AACH,KAAK,aAAa,GACd,8BAA8B,GAC9B,gCAAgC,GAChC,8BAA8B,GAC9B,+BAA+B,GAC/B,8BAA8B,GAC9B,gCAAgC,GAChC,kCAAkC,GAClC,iCAAiC,GACjC,4BAA4B,GAC5B,oDAAoD,GACpD,6CAA6C,GAC7C,6CAA6C,GAC7C,6CAA6C,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,SAAS,CACjD,OAAO,WAAW,EAClB,yBAAyB,GAAG,cAAc,EAC1C,wBAAwB,GAAG,aAAa,CACzC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,2BAA2B,CAAC;IACvC,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAeF;;GAEG;AACH,qBAAa,kBAAkB;;IAC7B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAC;IAQlC;;;;;;OAMG;gBACS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,yBAAyB;IA2F5D;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;OAMG;IACH,QAAQ,IAAI,MAAM,EAAE;IAIpB;;;;;;;;;OASG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD;;;;;OAKG;IACG,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAyCxD;;;;;;OAMG;IACG,wBAAwB,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC;CA+EjB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { SnapKeyring as LegacySnapKeyring, SnapMessage } from "@metamask/eth-snap-keyring";
|
|
1
|
+
import type { SnapKeyring as LegacySnapKeyring, SnapMessage } from "@metamask/eth-snap-keyring";
|
|
2
2
|
import type { KeyringControllerGetStateAction, KeyringControllerStateChangeEvent, KeyringControllerUnlockEvent, KeyringControllerWithControllerAction } from "@metamask/keyring-controller";
|
|
3
|
-
import { KeyringControllerWithKeyringUnsafeAction } from "@metamask/keyring-controller";
|
|
4
3
|
import type { Messenger } from "@metamask/messenger";
|
|
5
4
|
import type { SnapControllerGetRunnableSnapsAction, SnapControllerGetSnapAction, SnapControllerGetStateAction, SnapControllerSnapBlockedEvent, SnapControllerSnapDisabledEvent, SnapControllerSnapEnabledEvent, SnapControllerSnapInstalledEvent, SnapControllerSnapUnblockedEvent, SnapControllerSnapUninstalledEvent, SnapControllerStateChangeEvent } from "@metamask/snaps-controllers";
|
|
6
5
|
import { SnapId } from "@metamask/snaps-sdk";
|
|
@@ -20,7 +19,7 @@ export type SnapAccountServiceActions = SnapAccountServiceEnsureReadyAction | Sn
|
|
|
20
19
|
/**
|
|
21
20
|
* Actions from other messengers that {@link SnapAccountService} calls.
|
|
22
21
|
*/
|
|
23
|
-
type AllowedActions = SnapControllerGetStateAction | SnapControllerGetSnapAction | SnapControllerGetRunnableSnapsAction | KeyringControllerGetStateAction | KeyringControllerWithControllerAction |
|
|
22
|
+
type AllowedActions = SnapControllerGetStateAction | SnapControllerGetSnapAction | SnapControllerGetRunnableSnapsAction | KeyringControllerGetStateAction | KeyringControllerWithControllerAction | AccountTreeControllerGetAccountGroupObjectAction | AccountTreeControllerGetSelectedAccountGroupAction;
|
|
24
23
|
/**
|
|
25
24
|
* Events that {@link SnapAccountService} exposes to other consumers.
|
|
26
25
|
*/
|
|
@@ -64,6 +63,14 @@ export declare class SnapAccountService {
|
|
|
64
63
|
* @param args.config - Optional service configuration.
|
|
65
64
|
*/
|
|
66
65
|
constructor({ messenger, config }: SnapAccountServiceOptions);
|
|
66
|
+
/**
|
|
67
|
+
* Initializes the snap account service.
|
|
68
|
+
*
|
|
69
|
+
* Seeds the internal set of account-management Snaps from
|
|
70
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
71
|
+
* events.
|
|
72
|
+
*/
|
|
73
|
+
init(): Promise<void>;
|
|
67
74
|
/**
|
|
68
75
|
* Returns the IDs of all currently tracked account-management Snaps —
|
|
69
76
|
* Snaps that are installed, enabled, not blocked, and have the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapAccountService.d.mts","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"SnapAccountService.d.mts","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,IAAI,iBAAiB,EAChC,WAAW,EACZ,mCAAmC;AACpC,OAAO,KAAK,EACV,+BAA+B,EAC/B,iCAAiC,EACjC,4BAA4B,EAC5B,qCAAqC,EAEtC,qCAAqC;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AACrD,OAAO,KAAK,EACV,oCAAoC,EACpC,2BAA2B,EAC3B,4BAA4B,EAC5B,8BAA8B,EAC9B,+BAA+B,EAC/B,8BAA8B,EAC9B,gCAAgC,EAChC,gCAAgC,EAChC,kCAAkC,EAClC,8BAA8B,EAC/B,oCAAoC;AACrC,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,wBAAwB;AAG5C,OAAO,KAAK,EACV,mCAAmC,EACnC,4CAA4C,EAC5C,gCAAgC,EAChC,gDAAgD,EACjD,qDAAiD;AAElD,OAAO,KAAK,EAAE,yBAAyB,EAAE,kCAA8B;AAEvE,OAAO,KAAK,EACV,gDAAgD,EAChD,kDAAkD,EAClD,oDAAoD,EACpD,6CAA6C,EAC7C,6CAA6C,EAC7C,6CAA6C,EAE9C,oBAAgB;AAEjB;;;GAGG;AACH,eAAO,MAAM,WAAW,uBAAuB,CAAC;AAahD;;GAEG;AACH,MAAM,MAAM,yBAAyB,GACjC,mCAAmC,GACnC,gCAAgC,GAChC,4CAA4C,GAC5C,gDAAgD,CAAC;AAErD;;GAEG;AACH,KAAK,cAAc,GACf,4BAA4B,GAC5B,2BAA2B,GAC3B,oCAAoC,GACpC,+BAA+B,GAC/B,qCAAqC,GACrC,gDAAgD,GAChD,kDAAkD,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE7C;;GAEG;AACH,KAAK,aAAa,GACd,8BAA8B,GAC9B,gCAAgC,GAChC,8BAA8B,GAC9B,+BAA+B,GAC/B,8BAA8B,GAC9B,gCAAgC,GAChC,kCAAkC,GAClC,iCAAiC,GACjC,4BAA4B,GAC5B,oDAAoD,GACpD,6CAA6C,GAC7C,6CAA6C,GAC7C,6CAA6C,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG,SAAS,CACjD,OAAO,WAAW,EAClB,yBAAyB,GAAG,cAAc,EAC1C,wBAAwB,GAAG,aAAa,CACzC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,EAAE,2BAA2B,CAAC;IACvC,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAeF;;GAEG;AACH,qBAAa,kBAAkB;;IAC7B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAC;IAQlC;;;;;;OAMG;gBACS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,yBAAyB;IA2F5D;;;;;;OAMG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;;;;;OAMG;IACH,QAAQ,IAAI,MAAM,EAAE;IAIpB;;;;;;;;;OASG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD;;;;;OAKG;IACG,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAyCxD;;;;;;OAMG;IACG,wBAAwB,CAC5B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC;CA+EjB"}
|
|
@@ -9,11 +9,8 @@ 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 _SnapAccountService_instances, _SnapAccountService_messenger, _SnapAccountService_watcher, _SnapAccountService_tracker, _SnapAccountService_handleSelectedAccountGroupChange, _SnapAccountService_handleUnlock, _SnapAccountService_handleAccountGroupCreatedOrUpdated, _SnapAccountService_handleAccountGroupRemoved,
|
|
13
|
-
import {
|
|
14
|
-
import { KeyringEvent } from "@metamask/keyring-api";
|
|
15
|
-
import { isKeyringNotFoundError, KeyringTypes } from "@metamask/keyring-controller";
|
|
16
|
-
import { SnapManageAccountsMethod } from "@metamask/keyring-snap-sdk";
|
|
12
|
+
var _SnapAccountService_instances, _SnapAccountService_messenger, _SnapAccountService_watcher, _SnapAccountService_tracker, _SnapAccountService_handleSelectedAccountGroupChange, _SnapAccountService_handleUnlock, _SnapAccountService_handleAccountGroupCreatedOrUpdated, _SnapAccountService_handleAccountGroupRemoved, _SnapAccountService_forwardSelectedAccounts, _SnapAccountService_getAccountGroup, _SnapAccountService_getSelectedAccountGroupId;
|
|
13
|
+
import { KeyringTypes } from "@metamask/keyring-controller";
|
|
17
14
|
import { projectLogger as log } from "./logger.mjs";
|
|
18
15
|
import { SnapPlatformWatcher } from "./SnapPlatformWatcher.mjs";
|
|
19
16
|
import { SnapTracker } from "./SnapTracker.mjs";
|
|
@@ -69,6 +66,16 @@ export class SnapAccountService {
|
|
|
69
66
|
__classPrivateFieldGet(this, _SnapAccountService_messenger, "f").subscribe('AccountTreeController:accountGroupRemoved', (groupId) => __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_handleAccountGroupRemoved).call(this, groupId));
|
|
70
67
|
__classPrivateFieldGet(this, _SnapAccountService_messenger, "f").subscribe('KeyringController:unlock', () => __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_handleUnlock).call(this));
|
|
71
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Initializes the snap account service.
|
|
71
|
+
*
|
|
72
|
+
* Seeds the internal set of account-management Snaps from
|
|
73
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
74
|
+
* events.
|
|
75
|
+
*/
|
|
76
|
+
async init() {
|
|
77
|
+
await __classPrivateFieldGet(this, _SnapAccountService_tracker, "f").init();
|
|
78
|
+
}
|
|
72
79
|
/**
|
|
73
80
|
* Returns the IDs of all currently tracked account-management Snaps —
|
|
74
81
|
* Snaps that are installed, enabled, not blocked, and have the
|
|
@@ -104,14 +111,6 @@ export class SnapAccountService {
|
|
|
104
111
|
* @returns The existing or newly-created Snap keyring instance.
|
|
105
112
|
*/
|
|
106
113
|
async getLegacySnapKeyring() {
|
|
107
|
-
// This is a fast-path for the common case where the keyring already exists, to avoid the
|
|
108
|
-
// overhead of acquiring the `KeyringController` mutex if we don't need to.
|
|
109
|
-
// NOTE: If it doesn't exist, we'll create it **safely** with `:withController` (which was
|
|
110
|
-
// not the case with the previous client's implementation).
|
|
111
|
-
const exists = await __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_getLegacySnapKeyringIfAvailable).call(this);
|
|
112
|
-
if (exists) {
|
|
113
|
-
return exists;
|
|
114
|
-
}
|
|
115
114
|
// `KeyringController:withController` forbids returning a direct keyring
|
|
116
115
|
// reference (it checks the result via `Object.is`), so we smuggle the
|
|
117
116
|
// instance out wrapped in an object and unwrap it after the call.
|
|
@@ -142,31 +141,7 @@ export class SnapAccountService {
|
|
|
142
141
|
* @returns The execution result.
|
|
143
142
|
*/
|
|
144
143
|
async handleKeyringSnapMessage(snapId, message) {
|
|
145
|
-
|
|
146
|
-
// Handle specific methods first.
|
|
147
|
-
if (message.method === SnapManageAccountsMethod.GetSelectedAccounts) {
|
|
148
|
-
if (snapKeyring) {
|
|
149
|
-
// The legacy Snap keyring already maintain a local list of selected accounts per Snaps, so we
|
|
150
|
-
// just delegate the call.
|
|
151
|
-
return snapKeyring.handleKeyringSnapMessage(snapId, message);
|
|
152
|
-
}
|
|
153
|
-
// Some Snaps might be using `getSelectedAccounts` early in their lifecycle, before the keyring is created. So we
|
|
154
|
-
// do not throw in that case to avoid messing up their lifecycle.
|
|
155
|
-
return [];
|
|
156
|
-
}
|
|
157
|
-
const event = message.method; // We assume the Snap platform always sends a valid `KeyringEvent` here.
|
|
158
|
-
log(`Forwarding message "${event}" from Snap "${snapId}" to its keyring...`);
|
|
159
|
-
// We can create a new keyring if the message is an AccountCreated event.
|
|
160
|
-
const isAccountCreatedMessage = event === KeyringEvent.AccountCreated;
|
|
161
|
-
// Create the Snap keyring if it doesn't exist yet (in an atomic way). We cannot assume
|
|
162
|
-
// the keyring exists (e.g for the MMI Snap).
|
|
163
|
-
// NOTE: We only auto-create it for v1 account creation flows.
|
|
164
|
-
if (isAccountCreatedMessage && !snapKeyring) {
|
|
165
|
-
snapKeyring = await this.getLegacySnapKeyring();
|
|
166
|
-
}
|
|
167
|
-
if (!snapKeyring) {
|
|
168
|
-
throw new Error(`Legacy Snap keyring does not exist yet for snap "${snapId}".`);
|
|
169
|
-
}
|
|
144
|
+
const snapKeyring = await this.getLegacySnapKeyring();
|
|
170
145
|
return snapKeyring.handleKeyringSnapMessage(snapId, message);
|
|
171
146
|
}
|
|
172
147
|
}
|
|
@@ -183,27 +158,6 @@ _SnapAccountService_messenger = new WeakMap(), _SnapAccountService_watcher = new
|
|
|
183
158
|
if (groupId === __classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_getSelectedAccountGroupId).call(this)) {
|
|
184
159
|
__classPrivateFieldGet(this, _SnapAccountService_instances, "m", _SnapAccountService_forwardSelectedAccounts).call(this, groupId, []);
|
|
185
160
|
}
|
|
186
|
-
}, _SnapAccountService_getLegacySnapKeyringIfAvailable =
|
|
187
|
-
/**
|
|
188
|
-
* Gets the legacy (v1) Snap keyring but do not auto-create it if it doesn't exist.
|
|
189
|
-
*
|
|
190
|
-
* @returns The existing Snap keyring instance, or undefined if it doesn't exist.
|
|
191
|
-
*/
|
|
192
|
-
async function _SnapAccountService_getLegacySnapKeyringIfAvailable() {
|
|
193
|
-
try {
|
|
194
|
-
const result = await __classPrivateFieldGet(this, _SnapAccountService_messenger, "f").call('KeyringController:withKeyringUnsafe', { filter: isLegacySnapKeyring }, async ({ keyring }) => {
|
|
195
|
-
// The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.
|
|
196
|
-
return { snapKeyring: keyring };
|
|
197
|
-
});
|
|
198
|
-
return result.snapKeyring;
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
if (isKeyringNotFoundError(error)) {
|
|
202
|
-
log('Legacy Snap keyring not available yet.');
|
|
203
|
-
return undefined;
|
|
204
|
-
}
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
161
|
}, _SnapAccountService_forwardSelectedAccounts = function _SnapAccountService_forwardSelectedAccounts(groupId, accounts) {
|
|
208
162
|
if (!groupId) {
|
|
209
163
|
log('No selected account group, skipping forwarding selected accounts to Snap keyring.');
|
|
@@ -220,11 +174,7 @@ async function _SnapAccountService_getLegacySnapKeyringIfAvailable() {
|
|
|
220
174
|
else {
|
|
221
175
|
log(`Clearing selected accounts (from "${groupId}")`);
|
|
222
176
|
}
|
|
223
|
-
const snapKeyring = await
|
|
224
|
-
if (!snapKeyring) {
|
|
225
|
-
log('No legacy Snap keyring available, skipping forwarding selected accounts.');
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
177
|
+
const snapKeyring = await this.getLegacySnapKeyring();
|
|
228
178
|
await snapKeyring.setSelectedAccounts(accounts);
|
|
229
179
|
};
|
|
230
180
|
// There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapAccountService.mjs","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":"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","sourcesContent":["import { AccountGroupId } from '@metamask/account-api';\nimport {\n SnapKeyring as LegacySnapKeyring,\n SnapMessage,\n} from '@metamask/eth-snap-keyring';\nimport { KeyringEvent } from '@metamask/keyring-api';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithControllerAction,\n KeyringEntry,\n} from '@metamask/keyring-controller';\nimport {\n isKeyringNotFoundError,\n KeyringTypes,\n} from '@metamask/keyring-controller';\nimport { KeyringControllerWithKeyringUnsafeAction } from '@metamask/keyring-controller';\nimport { SnapManageAccountsMethod } from '@metamask/keyring-snap-sdk';\nimport type { AccountId, BaseKeyring } from '@metamask/keyring-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerGetSnapAction,\n SnapControllerGetStateAction,\n SnapControllerSnapBlockedEvent,\n SnapControllerSnapDisabledEvent,\n SnapControllerSnapEnabledEvent,\n SnapControllerSnapInstalledEvent,\n SnapControllerSnapUnblockedEvent,\n SnapControllerSnapUninstalledEvent,\n SnapControllerStateChangeEvent,\n} from '@metamask/snaps-controllers';\nimport { SnapId } from '@metamask/snaps-sdk';\nimport type { Json } from '@metamask/utils';\n\nimport { projectLogger as log } from './logger';\nimport type {\n SnapAccountServiceEnsureReadyAction,\n SnapAccountServiceGetLegacySnapKeyringAction,\n SnapAccountServiceGetSnapsAction,\n SnapAccountServiceHandleKeyringSnapMessageAction,\n} from './SnapAccountService-method-action-types';\nimport { SnapPlatformWatcher } from './SnapPlatformWatcher';\nimport type { SnapPlatformWatcherConfig } from './SnapPlatformWatcher';\nimport { SnapTracker } from './SnapTracker';\nimport type {\n AccountTreeControllerGetAccountGroupObjectAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\n AccountTreeControllerSelectedAccountGroupChangeEvent,\n AccountTreeControllerAccountGroupCreatedEvent,\n AccountTreeControllerAccountGroupUpdatedEvent,\n AccountTreeControllerAccountGroupRemovedEvent,\n AccountGroupObject,\n} from './types';\n\n/**\n * The name of the {@link SnapAccountService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'SnapAccountService';\n\n/**\n * All of the methods within {@link SnapAccountService} that are exposed via\n * the messenger.\n */\nconst MESSENGER_EXPOSED_METHODS = [\n 'ensureReady',\n 'getSnaps',\n 'getLegacySnapKeyring',\n 'handleKeyringSnapMessage',\n] as const;\n\n/**\n * Actions that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceActions =\n | SnapAccountServiceEnsureReadyAction\n | SnapAccountServiceGetSnapsAction\n | SnapAccountServiceGetLegacySnapKeyringAction\n | SnapAccountServiceHandleKeyringSnapMessageAction;\n\n/**\n * Actions from other messengers that {@link SnapAccountService} calls.\n */\ntype AllowedActions =\n | SnapControllerGetStateAction\n | SnapControllerGetSnapAction\n | SnapControllerGetRunnableSnapsAction\n | KeyringControllerGetStateAction\n | KeyringControllerWithControllerAction\n | KeyringControllerWithKeyringUnsafeAction\n | AccountTreeControllerGetAccountGroupObjectAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\n/**\n * Events that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceEvents = never;\n\n/**\n * Events from other messengers that {@link SnapAccountService} subscribes to.\n */\ntype AllowedEvents =\n | SnapControllerStateChangeEvent\n | SnapControllerSnapInstalledEvent\n | SnapControllerSnapEnabledEvent\n | SnapControllerSnapDisabledEvent\n | SnapControllerSnapBlockedEvent\n | SnapControllerSnapUnblockedEvent\n | SnapControllerSnapUninstalledEvent\n | KeyringControllerStateChangeEvent\n | KeyringControllerUnlockEvent\n | AccountTreeControllerSelectedAccountGroupChangeEvent\n | AccountTreeControllerAccountGroupCreatedEvent\n | AccountTreeControllerAccountGroupUpdatedEvent\n | AccountTreeControllerAccountGroupRemovedEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SnapAccountService}.\n */\nexport type SnapAccountServiceMessenger = Messenger<\n typeof serviceName,\n SnapAccountServiceActions | AllowedActions,\n SnapAccountServiceEvents | AllowedEvents\n>;\n\n/**\n * Configuration for the {@link SnapAccountService}.\n */\nexport type SnapAccountServiceConfig = {\n snapPlatformWatcher?: SnapPlatformWatcherConfig;\n};\n\n/**\n * The options that {@link SnapAccountService} takes.\n */\nexport type SnapAccountServiceOptions = {\n messenger: SnapAccountServiceMessenger;\n config?: SnapAccountServiceConfig;\n};\n\n/**\n * Checks if a given keyring is a Snap keyring (v2).\n *\n * @param keyring - The keyring to check.\n * @param keyring.type - The type of the keyring.\n * @returns `true` if the keyring is a Snap keyring (v2), `false` otherwise.\n */\nfunction isLegacySnapKeyring(keyring: {\n type: BaseKeyring['type'];\n}): keyring is LegacySnapKeyring {\n return keyring.type === KeyringTypes.snap;\n}\n\n/**\n * Service responsible for managing account management snaps.\n */\nexport class SnapAccountService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #watcher: SnapPlatformWatcher;\n\n readonly #tracker: SnapTracker;\n\n /**\n * Constructs a new {@link SnapAccountService}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.config - Optional service configuration.\n */\n constructor({ messenger, config }: SnapAccountServiceOptions) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#watcher = new SnapPlatformWatcher(\n messenger,\n config?.snapPlatformWatcher,\n );\n this.#tracker = new SnapTracker(messenger);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:selectedAccountGroupChange',\n (groupId) => this.#handleSelectedAccountGroupChange(groupId),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupCreated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupUpdated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupRemoved',\n (groupId) => this.#handleAccountGroupRemoved(groupId),\n );\n\n this.#messenger.subscribe('KeyringController:unlock', () =>\n this.#handleUnlock(),\n );\n }\n\n /**\n * Handles changes to the selected account group by forwarding the new\n * group's accounts to the Snap keyring.\n *\n * @param groupId - The ID of the newly selected account group.\n */\n #handleSelectedAccountGroupChange(groupId: AccountGroupId | ''): void {\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles the keyring controller unlock event by forwarding the currently\n * selected account group's accounts to the Snap keyring.\n */\n #handleUnlock(): void {\n const groupId = this.#getSelectedAccountGroupId();\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles created or updated account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the created/updated group is currently selected.\n *\n * @param group - The account group being created or updated.\n */\n #handleAccountGroupCreatedOrUpdated(group: AccountGroupObject): void {\n if (group.id === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(group.id, group.accounts);\n }\n }\n\n /**\n * Handles removed account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the removed group is currently selected.\n *\n * @param groupId - The ID of the account group being removed.\n */\n #handleAccountGroupRemoved(groupId: AccountGroupId): void {\n if (groupId === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(\n groupId,\n [], // Clearing accounts since the group is removed\n );\n }\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps —\n * Snaps that are installed, enabled, not blocked, and have the\n * `endowment:keyring` permission.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return this.#tracker.getSnaps();\n }\n\n /**\n * Ensures everything is ready to use Snap accounts for the given Snap.\n * 1. Validates that `snapId` is a tracked account-management Snap.\n * 2. Waits for the Snap platform to be fully started.\n *\n * Safe to call concurrently — each step is idempotent or mutex-protected.\n *\n * @param snapId - ID of the Snap to ensure readiness for.\n * @throws If `snapId` is not a tracked account-management Snap.\n */\n async ensureReady(snapId: SnapId): Promise<void> {\n if (!this.#tracker.canUse(snapId)) {\n throw new Error(`Unknown snap: \"${snapId}\"`);\n }\n // Before doing anything with our Snap, we need to make sure the platform\n // is ready to process requests.\n await this.#watcher.ensureCanUseSnapPlatform();\n }\n\n /**\n * Atomically gets-or-creates the legacy (v1) Snap keyring — the keyring\n * associated with {@link KeyringTypes.snap}.\n *\n * @returns The existing or newly-created Snap keyring instance.\n */\n async getLegacySnapKeyring(): Promise<LegacySnapKeyring> {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n // This is a fast-path for the common case where the keyring already exists, to avoid the\n // overhead of acquiring the `KeyringController` mutex if we don't need to.\n // NOTE: If it doesn't exist, we'll create it **safely** with `:withController` (which was\n // not the case with the previous client's implementation).\n const exists = await this.#getLegacySnapKeyringIfAvailable();\n if (exists) {\n return exists;\n }\n\n // `KeyringController:withController` forbids returning a direct keyring\n // reference (it checks the result via `Object.is`), so we smuggle the\n // instance out wrapped in an object and unwrap it after the call.\n // NOTE: This violates the abstraction of `KeyringController:withController`, but this\n // is how we currently interact with the legacy Snap keyring. Once we migrate it to\n // the Snap keyring v2, we won't be using the same pattern.\n const result = await this.#messenger.call(\n 'KeyringController:withController',\n async (controller): Promise<Result> => {\n let snapKeyring: KeyringEntry['keyring'] | undefined;\n\n const found = controller.keyrings.find(({ keyring }) =>\n isLegacySnapKeyring(keyring),\n );\n if (found) {\n snapKeyring = found.keyring;\n }\n\n if (!snapKeyring) {\n const {\n keyring: newSnapKeyring,\n metadata: { id },\n } = await controller.addNewKeyring(KeyringTypes.snap);\n snapKeyring = newSnapKeyring;\n\n log(`Legacy Snap keyring created. (\"${id}\")`);\n }\n\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n }\n\n /**\n * Gets the legacy (v1) Snap keyring but do not auto-create it if it doesn't exist.\n *\n * @returns The existing Snap keyring instance, or undefined if it doesn't exist.\n */\n async #getLegacySnapKeyringIfAvailable(): Promise<\n LegacySnapKeyring | undefined\n > {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n try {\n const result = await this.#messenger.call(\n 'KeyringController:withKeyringUnsafe',\n { filter: isLegacySnapKeyring },\n async ({ keyring }): Promise<Result> => {\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring: keyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n } catch (error) {\n if (isKeyringNotFoundError(error)) {\n log('Legacy Snap keyring not available yet.');\n return undefined;\n }\n\n throw error;\n }\n }\n\n /**\n * Handle a message from a Snap.\n *\n * @param snapId - ID of the Snap.\n * @param message - Message sent by the Snap.\n * @returns The execution result.\n */\n async handleKeyringSnapMessage(\n snapId: SnapId,\n message: SnapMessage,\n ): Promise<Json> {\n let snapKeyring: LegacySnapKeyring | undefined =\n await this.#getLegacySnapKeyringIfAvailable();\n\n // Handle specific methods first.\n if (message.method === SnapManageAccountsMethod.GetSelectedAccounts) {\n if (snapKeyring) {\n // The legacy Snap keyring already maintain a local list of selected accounts per Snaps, so we\n // just delegate the call.\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n // Some Snaps might be using `getSelectedAccounts` early in their lifecycle, before the keyring is created. So we\n // do not throw in that case to avoid messing up their lifecycle.\n return [];\n }\n\n const event = message.method as KeyringEvent; // We assume the Snap platform always sends a valid `KeyringEvent` here.\n log(\n `Forwarding message \"${event}\" from Snap \"${snapId}\" to its keyring...`,\n );\n\n // We can create a new keyring if the message is an AccountCreated event.\n const isAccountCreatedMessage = event === KeyringEvent.AccountCreated;\n\n // Create the Snap keyring if it doesn't exist yet (in an atomic way). We cannot assume\n // the keyring exists (e.g for the MMI Snap).\n // NOTE: We only auto-create it for v1 account creation flows.\n if (isAccountCreatedMessage && !snapKeyring) {\n snapKeyring = await this.getLegacySnapKeyring();\n }\n\n if (!snapKeyring) {\n throw new Error(\n `Legacy Snap keyring does not exist yet for snap \"${snapId}\".`,\n );\n }\n\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n /**\n * Forwards the accounts of the given account group to the Snap keyring.\n *\n * @param groupId - The ID of the account group whose accounts should be\n * forwarded. If empty, this is a no-op.\n * @param accounts - The accounts to forward. If not defined, this is a no-op.\n */\n #forwardSelectedAccounts(\n groupId: AccountGroupId | '',\n accounts: AccountId[] | undefined,\n ): void {\n if (!groupId) {\n log(\n 'No selected account group, skipping forwarding selected accounts to Snap keyring.',\n );\n return;\n }\n\n if (!accounts) {\n log(\n `Account group (\"${groupId}\") has no accounts, skipping forwarding selected accounts to Snap keyring.`,\n );\n return;\n }\n\n const forwardSelectedAccounts = async (): Promise<void> => {\n if (accounts.length) {\n log(\n `Forwarding selected accounts (from \"${groupId}\"): ${accounts.join(', ')}`,\n );\n } else {\n log(`Clearing selected accounts (from \"${groupId}\")`);\n }\n\n const snapKeyring = await this.#getLegacySnapKeyringIfAvailable();\n if (!snapKeyring) {\n log(\n 'No legacy Snap keyring available, skipping forwarding selected accounts.',\n );\n return;\n }\n\n await snapKeyring.setSelectedAccounts(accounts);\n };\n\n // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.\n forwardSelectedAccounts().catch((error) => {\n console.error('Error forwarding selected accounts:', error);\n });\n }\n\n /**\n * Gets the account group object for the given group ID.\n *\n * @param groupId - The ID of the account group.\n * @returns The account group object, or undefined if the group ID is empty or the group does not exist.\n */\n #getAccountGroup(\n groupId: AccountGroupId | '',\n ): AccountGroupObject | undefined {\n if (!groupId) {\n return undefined;\n }\n\n return this.#messenger.call(\n 'AccountTreeController:getAccountGroupObject',\n groupId,\n );\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The currently selected account group ID, or an empty string if\n * there is no selected account group.\n */\n #getSelectedAccountGroupId(): AccountGroupId | '' {\n return this.#messenger.call(\n 'AccountTreeController:getSelectedAccountGroup',\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"SnapAccountService.mjs","sourceRoot":"","sources":["../src/SnapAccountService.ts"],"names":[],"mappings":";;;;;;;;;;;;AAYA,OAAO,EAAE,YAAY,EAAE,qCAAqC;AAkB5D,OAAO,EAAE,aAAa,IAAI,GAAG,EAAE,qBAAiB;AAOhD,OAAO,EAAE,mBAAmB,EAAE,kCAA8B;AAE5D,OAAO,EAAE,WAAW,EAAE,0BAAsB;AAW5C;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEhD;;;GAGG;AACH,MAAM,yBAAyB,GAAG;IAChC,aAAa;IACb,UAAU;IACV,sBAAsB;IACtB,0BAA0B;CAClB,CAAC;AAuEX;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,OAE5B;IACC,OAAO,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAY7B;;;;;;OAMG;IACH,YAAY,EAAE,SAAS,EAAE,MAAM,EAA6B;;QAbnD,gDAAwC;QAExC,8CAA8B;QAE9B,8CAAsB;QAU7B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,uBAAA,IAAI,iCAAc,SAAS,MAAA,CAAC;QAC5B,uBAAA,IAAI,+BAAY,IAAI,mBAAmB,CACrC,SAAS,EACT,MAAM,EAAE,mBAAmB,CAC5B,MAAA,CAAC;QACF,uBAAA,IAAI,+BAAY,IAAI,WAAW,CAAC,SAAS,CAAC,MAAA,CAAC;QAE3C,uBAAA,IAAI,qCAAW,CAAC,4BAA4B,CAC1C,IAAI,EACJ,yBAAyB,CAC1B,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,kDAAkD,EAClD,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,2FAAkC,MAAtC,IAAI,EAAmC,OAAO,CAAC,CAC7D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAA,IAAI,6FAAoC,MAAxC,IAAI,EAAqC,KAAK,CAAC,CAC3D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAA,IAAI,6FAAoC,MAAxC,IAAI,EAAqC,KAAK,CAAC,CAC3D,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CACvB,2CAA2C,EAC3C,CAAC,OAAO,EAAE,EAAE,CAAC,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,EAA4B,OAAO,CAAC,CACtD,CAAC;QAEF,uBAAA,IAAI,qCAAW,CAAC,SAAS,CAAC,0BAA0B,EAAE,GAAG,EAAE,CACzD,uBAAA,IAAI,uEAAc,MAAlB,IAAI,CAAgB,CACrB,CAAC;IACJ,CAAC;IAsDD;;;;;;OAMG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,uBAAA,IAAI,mCAAS,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED;;;;;;OAMG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,mCAAS,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,WAAW,CAAC,MAAc;QAC9B,IAAI,CAAC,uBAAA,IAAI,mCAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,GAAG,CAAC,CAAC;QAC/C,CAAC;QACD,yEAAyE;QACzE,gCAAgC;QAChC,MAAM,uBAAA,IAAI,mCAAS,CAAC,wBAAwB,EAAE,CAAC;IACjD,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,oBAAoB;QAKxB,wEAAwE;QACxE,sEAAsE;QACtE,kEAAkE;QAClE,sFAAsF;QACtF,mFAAmF;QACnF,2DAA2D;QAC3D,MAAM,MAAM,GAAG,MAAM,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACvC,kCAAkC,EAClC,KAAK,EAAE,UAAU,EAAmB,EAAE;YACpC,IAAI,WAAgD,CAAC;YAErD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CACrD,mBAAmB,CAAC,OAAO,CAAC,CAC7B,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC;YAC9B,CAAC;YAED,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,EACJ,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,EAAE,EAAE,EAAE,GACjB,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACtD,WAAW,GAAG,cAAc,CAAC;gBAE7B,GAAG,CAAC,kCAAkC,EAAE,IAAI,CAAC,CAAC;YAChD,CAAC;YAED,wFAAwF;YACxF,OAAO,EAAE,WAAW,EAAuB,CAAC;QAC9C,CAAC,CACF,CAAC;QAEF,OAAQ,MAAiB,CAAC,WAAW,CAAC;IACxC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,wBAAwB,CAC5B,MAAc,EACd,OAAoB;QAEpB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACtD,OAAO,WAAW,CAAC,wBAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;CA4EF;6SA/NmC,OAA4B;IAC5D,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,uBAAA,IAAI,0EAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,EAAE,QAAQ,CACzC,CAAC;AACJ,CAAC;IAOC,MAAM,OAAO,GAAG,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,CAAC;IAClD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,uBAAA,IAAI,0EAAiB,MAArB,IAAI,EAAkB,OAAO,CAAC,EAAE,QAAQ,CACzC,CAAC;AACJ,CAAC,2HAQmC,KAAyB;IAC3D,IAAI,KAAK,CAAC,EAAE,KAAK,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,EAAE,CAAC;QACnD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EAA0B,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC,yGAQ0B,OAAuB;IAChD,IAAI,OAAO,KAAK,uBAAA,IAAI,oFAA2B,MAA/B,IAAI,CAA6B,EAAE,CAAC;QAClD,uBAAA,IAAI,kFAAyB,MAA7B,IAAI,EACF,OAAO,EACP,EAAE,CACH,CAAC;IACJ,CAAC;AACH,CAAC,qGAiHC,OAA4B,EAC5B,QAAiC;IAEjC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,GAAG,CACD,mFAAmF,CACpF,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,GAAG,CACD,mBAAmB,OAAO,4EAA4E,CACvG,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,uBAAuB,GAAG,KAAK,IAAmB,EAAE;QACxD,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACpB,GAAG,CACD,uCAAuC,OAAO,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3E,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,qCAAqC,OAAO,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACtD,MAAM,WAAW,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC,CAAC;IAEF,qGAAqG;IACrG,uBAAuB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,qFASC,OAA4B;IAE5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACzB,6CAA6C,EAC7C,OAAO,CACR,CAAC;AACJ,CAAC;IASC,OAAO,uBAAA,IAAI,qCAAW,CAAC,IAAI,CACzB,+CAA+C,CAChD,CAAC;AACJ,CAAC","sourcesContent":["import { AccountGroupId } from '@metamask/account-api';\nimport type {\n SnapKeyring as LegacySnapKeyring,\n SnapMessage,\n} from '@metamask/eth-snap-keyring';\nimport type {\n KeyringControllerGetStateAction,\n KeyringControllerStateChangeEvent,\n KeyringControllerUnlockEvent,\n KeyringControllerWithControllerAction,\n KeyringEntry,\n} from '@metamask/keyring-controller';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type { AccountId, BaseKeyring } from '@metamask/keyring-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerGetSnapAction,\n SnapControllerGetStateAction,\n SnapControllerSnapBlockedEvent,\n SnapControllerSnapDisabledEvent,\n SnapControllerSnapEnabledEvent,\n SnapControllerSnapInstalledEvent,\n SnapControllerSnapUnblockedEvent,\n SnapControllerSnapUninstalledEvent,\n SnapControllerStateChangeEvent,\n} from '@metamask/snaps-controllers';\nimport { SnapId } from '@metamask/snaps-sdk';\nimport type { Json } from '@metamask/utils';\n\nimport { projectLogger as log } from './logger';\nimport type {\n SnapAccountServiceEnsureReadyAction,\n SnapAccountServiceGetLegacySnapKeyringAction,\n SnapAccountServiceGetSnapsAction,\n SnapAccountServiceHandleKeyringSnapMessageAction,\n} from './SnapAccountService-method-action-types';\nimport { SnapPlatformWatcher } from './SnapPlatformWatcher';\nimport type { SnapPlatformWatcherConfig } from './SnapPlatformWatcher';\nimport { SnapTracker } from './SnapTracker';\nimport type {\n AccountTreeControllerGetAccountGroupObjectAction,\n AccountTreeControllerGetSelectedAccountGroupAction,\n AccountTreeControllerSelectedAccountGroupChangeEvent,\n AccountTreeControllerAccountGroupCreatedEvent,\n AccountTreeControllerAccountGroupUpdatedEvent,\n AccountTreeControllerAccountGroupRemovedEvent,\n AccountGroupObject,\n} from './types';\n\n/**\n * The name of the {@link SnapAccountService}, used to namespace the service's\n * actions and events.\n */\nexport const serviceName = 'SnapAccountService';\n\n/**\n * All of the methods within {@link SnapAccountService} that are exposed via\n * the messenger.\n */\nconst MESSENGER_EXPOSED_METHODS = [\n 'ensureReady',\n 'getSnaps',\n 'getLegacySnapKeyring',\n 'handleKeyringSnapMessage',\n] as const;\n\n/**\n * Actions that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceActions =\n | SnapAccountServiceEnsureReadyAction\n | SnapAccountServiceGetSnapsAction\n | SnapAccountServiceGetLegacySnapKeyringAction\n | SnapAccountServiceHandleKeyringSnapMessageAction;\n\n/**\n * Actions from other messengers that {@link SnapAccountService} calls.\n */\ntype AllowedActions =\n | SnapControllerGetStateAction\n | SnapControllerGetSnapAction\n | SnapControllerGetRunnableSnapsAction\n | KeyringControllerGetStateAction\n | KeyringControllerWithControllerAction\n | AccountTreeControllerGetAccountGroupObjectAction\n | AccountTreeControllerGetSelectedAccountGroupAction;\n\n/**\n * Events that {@link SnapAccountService} exposes to other consumers.\n */\nexport type SnapAccountServiceEvents = never;\n\n/**\n * Events from other messengers that {@link SnapAccountService} subscribes to.\n */\ntype AllowedEvents =\n | SnapControllerStateChangeEvent\n | SnapControllerSnapInstalledEvent\n | SnapControllerSnapEnabledEvent\n | SnapControllerSnapDisabledEvent\n | SnapControllerSnapBlockedEvent\n | SnapControllerSnapUnblockedEvent\n | SnapControllerSnapUninstalledEvent\n | KeyringControllerStateChangeEvent\n | KeyringControllerUnlockEvent\n | AccountTreeControllerSelectedAccountGroupChangeEvent\n | AccountTreeControllerAccountGroupCreatedEvent\n | AccountTreeControllerAccountGroupUpdatedEvent\n | AccountTreeControllerAccountGroupRemovedEvent;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SnapAccountService}.\n */\nexport type SnapAccountServiceMessenger = Messenger<\n typeof serviceName,\n SnapAccountServiceActions | AllowedActions,\n SnapAccountServiceEvents | AllowedEvents\n>;\n\n/**\n * Configuration for the {@link SnapAccountService}.\n */\nexport type SnapAccountServiceConfig = {\n snapPlatformWatcher?: SnapPlatformWatcherConfig;\n};\n\n/**\n * The options that {@link SnapAccountService} takes.\n */\nexport type SnapAccountServiceOptions = {\n messenger: SnapAccountServiceMessenger;\n config?: SnapAccountServiceConfig;\n};\n\n/**\n * Checks if a given keyring is a Snap keyring (v2).\n *\n * @param keyring - The keyring to check.\n * @param keyring.type - The type of the keyring.\n * @returns `true` if the keyring is a Snap keyring (v2), `false` otherwise.\n */\nfunction isLegacySnapKeyring(keyring: {\n type: BaseKeyring['type'];\n}): keyring is LegacySnapKeyring {\n return keyring.type === KeyringTypes.snap;\n}\n\n/**\n * Service responsible for managing account management snaps.\n */\nexport class SnapAccountService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #watcher: SnapPlatformWatcher;\n\n readonly #tracker: SnapTracker;\n\n /**\n * Constructs a new {@link SnapAccountService}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.config - Optional service configuration.\n */\n constructor({ messenger, config }: SnapAccountServiceOptions) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#watcher = new SnapPlatformWatcher(\n messenger,\n config?.snapPlatformWatcher,\n );\n this.#tracker = new SnapTracker(messenger);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:selectedAccountGroupChange',\n (groupId) => this.#handleSelectedAccountGroupChange(groupId),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupCreated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupUpdated',\n (group) => this.#handleAccountGroupCreatedOrUpdated(group),\n );\n\n this.#messenger.subscribe(\n 'AccountTreeController:accountGroupRemoved',\n (groupId) => this.#handleAccountGroupRemoved(groupId),\n );\n\n this.#messenger.subscribe('KeyringController:unlock', () =>\n this.#handleUnlock(),\n );\n }\n\n /**\n * Handles changes to the selected account group by forwarding the new\n * group's accounts to the Snap keyring.\n *\n * @param groupId - The ID of the newly selected account group.\n */\n #handleSelectedAccountGroupChange(groupId: AccountGroupId | ''): void {\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles the keyring controller unlock event by forwarding the currently\n * selected account group's accounts to the Snap keyring.\n */\n #handleUnlock(): void {\n const groupId = this.#getSelectedAccountGroupId();\n this.#forwardSelectedAccounts(\n groupId,\n this.#getAccountGroup(groupId)?.accounts,\n );\n }\n\n /**\n * Handles created or updated account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the created/updated group is currently selected.\n *\n * @param group - The account group being created or updated.\n */\n #handleAccountGroupCreatedOrUpdated(group: AccountGroupObject): void {\n if (group.id === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(group.id, group.accounts);\n }\n }\n\n /**\n * Handles removed account groups by forwarding the accounts of the currently\n * selected account group to the Snap keyring, if the removed group is currently selected.\n *\n * @param groupId - The ID of the account group being removed.\n */\n #handleAccountGroupRemoved(groupId: AccountGroupId): void {\n if (groupId === this.#getSelectedAccountGroupId()) {\n this.#forwardSelectedAccounts(\n groupId,\n [], // Clearing accounts since the group is removed\n );\n }\n }\n\n /**\n * Initializes the snap account service.\n *\n * Seeds the internal set of account-management Snaps from\n * `SnapController:getRunnableSnaps`, then starts processing lifecycle\n * events.\n */\n async init(): Promise<void> {\n await this.#tracker.init();\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps —\n * Snaps that are installed, enabled, not blocked, and have the\n * `endowment:keyring` permission.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return this.#tracker.getSnaps();\n }\n\n /**\n * Ensures everything is ready to use Snap accounts for the given Snap.\n * 1. Validates that `snapId` is a tracked account-management Snap.\n * 2. Waits for the Snap platform to be fully started.\n *\n * Safe to call concurrently — each step is idempotent or mutex-protected.\n *\n * @param snapId - ID of the Snap to ensure readiness for.\n * @throws If `snapId` is not a tracked account-management Snap.\n */\n async ensureReady(snapId: SnapId): Promise<void> {\n if (!this.#tracker.canUse(snapId)) {\n throw new Error(`Unknown snap: \"${snapId}\"`);\n }\n // Before doing anything with our Snap, we need to make sure the platform\n // is ready to process requests.\n await this.#watcher.ensureCanUseSnapPlatform();\n }\n\n /**\n * Atomically gets-or-creates the legacy (v1) Snap keyring — the keyring\n * associated with {@link KeyringTypes.snap}.\n *\n * @returns The existing or newly-created Snap keyring instance.\n */\n async getLegacySnapKeyring(): Promise<LegacySnapKeyring> {\n type Result = {\n snapKeyring: LegacySnapKeyring;\n };\n\n // `KeyringController:withController` forbids returning a direct keyring\n // reference (it checks the result via `Object.is`), so we smuggle the\n // instance out wrapped in an object and unwrap it after the call.\n // NOTE: This violates the abstraction of `KeyringController:withController`, but this\n // is how we currently interact with the legacy Snap keyring. Once we migrate it to\n // the Snap keyring v2, we won't be using the same pattern.\n const result = await this.#messenger.call(\n 'KeyringController:withController',\n async (controller): Promise<Result> => {\n let snapKeyring: KeyringEntry['keyring'] | undefined;\n\n const found = controller.keyrings.find(({ keyring }) =>\n isLegacySnapKeyring(keyring),\n );\n if (found) {\n snapKeyring = found.keyring;\n }\n\n if (!snapKeyring) {\n const {\n keyring: newSnapKeyring,\n metadata: { id },\n } = await controller.addNewKeyring(KeyringTypes.snap);\n snapKeyring = newSnapKeyring;\n\n log(`Legacy Snap keyring created. (\"${id}\")`);\n }\n\n // The legacy Snap keyring is not compatible with `EthKeyring`, so we need to cast here.\n return { snapKeyring } as unknown as Result;\n },\n );\n\n return (result as Result).snapKeyring;\n }\n\n /**\n * Handle a message from a Snap.\n *\n * @param snapId - ID of the Snap.\n * @param message - Message sent by the Snap.\n * @returns The execution result.\n */\n async handleKeyringSnapMessage(\n snapId: SnapId,\n message: SnapMessage,\n ): Promise<Json> {\n const snapKeyring = await this.getLegacySnapKeyring();\n return snapKeyring.handleKeyringSnapMessage(snapId, message);\n }\n\n /**\n * Forwards the accounts of the given account group to the Snap keyring.\n *\n * @param groupId - The ID of the account group whose accounts should be\n * forwarded. If empty, this is a no-op.\n * @param accounts - The accounts to forward. If not defined, this is a no-op.\n */\n #forwardSelectedAccounts(\n groupId: AccountGroupId | '',\n accounts: AccountId[] | undefined,\n ): void {\n if (!groupId) {\n log(\n 'No selected account group, skipping forwarding selected accounts to Snap keyring.',\n );\n return;\n }\n\n if (!accounts) {\n log(\n `Account group (\"${groupId}\") has no accounts, skipping forwarding selected accounts to Snap keyring.`,\n );\n return;\n }\n\n const forwardSelectedAccounts = async (): Promise<void> => {\n if (accounts.length) {\n log(\n `Forwarding selected accounts (from \"${groupId}\"): ${accounts.join(', ')}`,\n );\n } else {\n log(`Clearing selected accounts (from \"${groupId}\")`);\n }\n\n const snapKeyring = await this.getLegacySnapKeyring();\n await snapKeyring.setSelectedAccounts(accounts);\n };\n\n // There is nothing we can do if forwarding fails. This will auto-recover on the next relevant event.\n forwardSelectedAccounts().catch((error) => {\n console.error('Error forwarding selected accounts:', error);\n });\n }\n\n /**\n * Gets the account group object for the given group ID.\n *\n * @param groupId - The ID of the account group.\n * @returns The account group object, or undefined if the group ID is empty or the group does not exist.\n */\n #getAccountGroup(\n groupId: AccountGroupId | '',\n ): AccountGroupObject | undefined {\n if (!groupId) {\n return undefined;\n }\n\n return this.#messenger.call(\n 'AccountTreeController:getAccountGroupObject',\n groupId,\n );\n }\n\n /**\n * Gets the currently selected account group ID.\n *\n * @returns The currently selected account group ID, or an empty string if\n * there is no selected account group.\n */\n #getSelectedAccountGroupId(): AccountGroupId | '' {\n return this.#messenger.call(\n 'AccountTreeController:getSelectedAccountGroup',\n );\n }\n}\n"]}
|
package/dist/SnapTracker.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _SnapTracker_instances, _SnapTracker_messenger, _SnapTracker_snaps,
|
|
13
|
+
var _SnapTracker_instances, _SnapTracker_messenger, _SnapTracker_snaps, _SnapTracker_initialized, _SnapTracker_handleSnapAdded, _SnapTracker_handleSnapUnblocked, _SnapTracker_handleSnapRemoved;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.SnapTracker = void 0;
|
|
16
16
|
const logger_1 = require("./logger.cjs");
|
|
@@ -34,6 +34,7 @@ class SnapTracker {
|
|
|
34
34
|
_SnapTracker_instances.add(this);
|
|
35
35
|
_SnapTracker_messenger.set(this, void 0);
|
|
36
36
|
_SnapTracker_snaps.set(this, new Set());
|
|
37
|
+
_SnapTracker_initialized.set(this, false);
|
|
37
38
|
__classPrivateFieldSet(this, _SnapTracker_messenger, messenger, "f");
|
|
38
39
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapInstalled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapAdded).call(this, snap, 'installed'));
|
|
39
40
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapUninstalled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snap.id, 'uninstalled'));
|
|
@@ -41,7 +42,27 @@ class SnapTracker {
|
|
|
41
42
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapDisabled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snap.id, 'disabled'));
|
|
42
43
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapBlocked', (snapId) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snapId, 'blocked'));
|
|
43
44
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapUnblocked', (snapId) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapUnblocked).call(this, snapId));
|
|
44
|
-
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Seeds the internal set of account-management Snaps from
|
|
48
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
49
|
+
* events.
|
|
50
|
+
*/
|
|
51
|
+
async init() {
|
|
52
|
+
if (__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
53
|
+
// Do not re-init, once setup we only rely on lifecycle events to update the set of
|
|
54
|
+
// tracked Snaps.
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").clear();
|
|
58
|
+
const runnable = __classPrivateFieldGet(this, _SnapTracker_messenger, "f").call('SnapController:getRunnableSnaps');
|
|
59
|
+
for (const snap of runnable) {
|
|
60
|
+
if (isAccountManagementSnap(snap)) {
|
|
61
|
+
(0, logger_1.projectLogger)(`Found account management Snap: ${snap.id} (initialization)`);
|
|
62
|
+
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
__classPrivateFieldSet(this, _SnapTracker_initialized, true, "f");
|
|
45
66
|
}
|
|
46
67
|
/**
|
|
47
68
|
* Returns the IDs of all currently tracked account-management Snaps.
|
|
@@ -62,15 +83,10 @@ class SnapTracker {
|
|
|
62
83
|
}
|
|
63
84
|
}
|
|
64
85
|
exports.SnapTracker = SnapTracker;
|
|
65
|
-
_SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _SnapTracker_instances = new WeakSet(),
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (isAccountManagementSnap(snap)) {
|
|
69
|
-
(0, logger_1.projectLogger)(`Found account management Snap: ${snap.id} (initialization)`);
|
|
70
|
-
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
71
|
-
}
|
|
86
|
+
_SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _SnapTracker_initialized = new WeakMap(), _SnapTracker_instances = new WeakSet(), _SnapTracker_handleSnapAdded = function _SnapTracker_handleSnapAdded(snap, reason) {
|
|
87
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
88
|
+
return;
|
|
72
89
|
}
|
|
73
|
-
}, _SnapTracker_handleSnapAdded = function _SnapTracker_handleSnapAdded(snap, reason) {
|
|
74
90
|
if (!snap.enabled || snap.blocked) {
|
|
75
91
|
return;
|
|
76
92
|
}
|
|
@@ -79,11 +95,17 @@ _SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _Sna
|
|
|
79
95
|
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
80
96
|
}
|
|
81
97
|
}, _SnapTracker_handleSnapUnblocked = function _SnapTracker_handleSnapUnblocked(snapId) {
|
|
98
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
82
101
|
const snap = __classPrivateFieldGet(this, _SnapTracker_messenger, "f").call('SnapController:getSnap', snapId);
|
|
83
102
|
if (snap) {
|
|
84
103
|
__classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapAdded).call(this, snap, 'unblocked');
|
|
85
104
|
}
|
|
86
105
|
}, _SnapTracker_handleSnapRemoved = function _SnapTracker_handleSnapRemoved(snapId, reason) {
|
|
106
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
87
109
|
if (__classPrivateFieldGet(this, _SnapTracker_snaps, "f").has(snapId)) {
|
|
88
110
|
(0, logger_1.projectLogger)(`Removed account management Snap: ${snapId} (${reason})`);
|
|
89
111
|
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").delete(snapId);
|
package/dist/SnapTracker.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapTracker.cjs","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAGA,yCAAgD;AAGhD;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,IAAmB;IAClD,OAAO,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,KAAK,SAAS,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAa,WAAW;
|
|
1
|
+
{"version":3,"file":"SnapTracker.cjs","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import { SnapId } from '@metamask/snaps-sdk';\nimport type { TruncatedSnap } from '@metamask/snaps-utils';\n\nimport { projectLogger as log } from './logger';\nimport type { SnapAccountServiceMessenger } from './SnapAccountService';\n\n/**\n * Checks if a given Snap is an account management Snap.\n *\n * @param snap - The Snap to check.\n * @returns True if the Snap declares the `endowment:keyring` initial\n * permission.\n */\nfunction isAccountManagementSnap(snap: TruncatedSnap): boolean {\n return snap.initialPermissions['endowment:keyring'] !== undefined;\n}\n\n/**\n * Tracks the set of installed, enabled, non-blocked account-management Snaps\n * (Snaps declaring the `endowment:keyring` initial permission) by listening to\n * `SnapController` lifecycle events.\n */\nexport class SnapTracker {\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #snaps: Set<SnapId> = new Set();\n\n #initialized = false;\n\n constructor(messenger: SnapAccountServiceMessenger) {\n this.#messenger = messenger;\n\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) =>\n this.#handleSnapAdded(snap, 'installed'),\n );\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) =>\n this.#handleSnapRemoved(snap.id, 'uninstalled'),\n );\n this.#messenger.subscribe('SnapController:snapEnabled', (snap) =>\n this.#handleSnapAdded(snap, 'enabled'),\n );\n this.#messenger.subscribe('SnapController:snapDisabled', (snap) =>\n this.#handleSnapRemoved(snap.id, 'disabled'),\n );\n this.#messenger.subscribe('SnapController:snapBlocked', (snapId) =>\n this.#handleSnapRemoved(snapId as SnapId, 'blocked'),\n );\n this.#messenger.subscribe('SnapController:snapUnblocked', (snapId) =>\n this.#handleSnapUnblocked(snapId as SnapId),\n );\n }\n\n /**\n * Seeds the internal set of account-management Snaps from\n * `SnapController:getRunnableSnaps`, then starts processing lifecycle\n * events.\n */\n async init(): Promise<void> {\n if (this.#initialized) {\n // Do not re-init, once setup we only rely on lifecycle events to update the set of\n // tracked Snaps.\n return;\n }\n\n this.#snaps.clear();\n\n const runnable = this.#messenger.call('SnapController:getRunnableSnaps');\n for (const snap of runnable) {\n if (isAccountManagementSnap(snap)) {\n log(`Found account management Snap: ${snap.id} (initialization)`);\n this.#snaps.add(snap.id);\n }\n }\n\n this.#initialized = true;\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return [...this.#snaps];\n }\n\n /**\n * Returns true if the given Snap ID is currently tracked and can be used.\n *\n * @param snapId - The Snap ID to check.\n * @returns True if the Snap is tracked and can be used.\n */\n canUse(snapId: SnapId): boolean {\n return this.#snaps.has(snapId);\n }\n\n /**\n * Handles a Snap being added (installed or enabled). If the Snap is an\n * account-management Snap, adds it to the internal set of tracked Snaps.\n *\n * @param snap - The Snap that was installed or enabled.\n * @param reason - The reason the Snap was added.\n */\n #handleSnapAdded(snap: TruncatedSnap, reason: string): void {\n if (!this.#initialized) {\n return;\n }\n\n if (!snap.enabled || snap.blocked) {\n return;\n }\n\n if (isAccountManagementSnap(snap) && !this.#snaps.has(snap.id)) {\n log(`Added account management Snap: ${snap.id} (${reason})`);\n\n this.#snaps.add(snap.id);\n }\n }\n\n /**\n * Handles a Snap being unblocked. If the Snap is an enabled\n * account-management Snap, re-adds it to the internal set of tracked Snaps.\n *\n * @param snapId - The Snap ID that was unblocked.\n */\n #handleSnapUnblocked(snapId: SnapId): void {\n if (!this.#initialized) {\n return;\n }\n\n const snap = this.#messenger.call('SnapController:getSnap', snapId);\n if (snap) {\n this.#handleSnapAdded(snap, 'unblocked');\n }\n }\n\n /**\n * Handles a Snap being removed (disabled, blocked, or uninstalled). If the Snap is an\n * account-management Snap, removes it from the internal set of tracked Snaps.\n *\n * @param snapId - The Snap ID that was disabled, blocked, or uninstalled.\n * @param reason - The reason the Snap was removed.\n */\n #handleSnapRemoved(snapId: SnapId, reason: string): void {\n if (!this.#initialized) {\n return;\n }\n\n if (this.#snaps.has(snapId)) {\n log(`Removed account management Snap: ${snapId} (${reason})`);\n\n this.#snaps.delete(snapId);\n }\n }\n}\n"]}
|
package/dist/SnapTracker.d.cts
CHANGED
|
@@ -8,6 +8,12 @@ import type { SnapAccountServiceMessenger } from "./SnapAccountService.cjs";
|
|
|
8
8
|
export declare class SnapTracker {
|
|
9
9
|
#private;
|
|
10
10
|
constructor(messenger: SnapAccountServiceMessenger);
|
|
11
|
+
/**
|
|
12
|
+
* Seeds the internal set of account-management Snaps from
|
|
13
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
14
|
+
* events.
|
|
15
|
+
*/
|
|
16
|
+
init(): Promise<void>;
|
|
11
17
|
/**
|
|
12
18
|
* Returns the IDs of all currently tracked account-management Snaps.
|
|
13
19
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapTracker.d.cts","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAI7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iCAA6B;AAaxE;;;;GAIG;AACH,qBAAa,WAAW;;
|
|
1
|
+
{"version":3,"file":"SnapTracker.d.cts","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAI7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iCAA6B;AAaxE;;;;GAIG;AACH,qBAAa,WAAW;;gBAOV,SAAS,EAAE,2BAA2B;IAuBlD;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;;OAIG;IACH,QAAQ,IAAI,MAAM,EAAE;IAIpB;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CA8DhC"}
|
package/dist/SnapTracker.d.mts
CHANGED
|
@@ -8,6 +8,12 @@ import type { SnapAccountServiceMessenger } from "./SnapAccountService.mjs";
|
|
|
8
8
|
export declare class SnapTracker {
|
|
9
9
|
#private;
|
|
10
10
|
constructor(messenger: SnapAccountServiceMessenger);
|
|
11
|
+
/**
|
|
12
|
+
* Seeds the internal set of account-management Snaps from
|
|
13
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
14
|
+
* events.
|
|
15
|
+
*/
|
|
16
|
+
init(): Promise<void>;
|
|
11
17
|
/**
|
|
12
18
|
* Returns the IDs of all currently tracked account-management Snaps.
|
|
13
19
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapTracker.d.mts","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAI7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iCAA6B;AAaxE;;;;GAIG;AACH,qBAAa,WAAW;;
|
|
1
|
+
{"version":3,"file":"SnapTracker.d.mts","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,4BAA4B;AAI7C,OAAO,KAAK,EAAE,2BAA2B,EAAE,iCAA6B;AAaxE;;;;GAIG;AACH,qBAAa,WAAW;;gBAOV,SAAS,EAAE,2BAA2B;IAuBlD;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;;OAIG;IACH,QAAQ,IAAI,MAAM,EAAE;IAIpB;;;;;OAKG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CA8DhC"}
|
package/dist/SnapTracker.mjs
CHANGED
|
@@ -9,7 +9,7 @@ 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 _SnapTracker_instances, _SnapTracker_messenger, _SnapTracker_snaps,
|
|
12
|
+
var _SnapTracker_instances, _SnapTracker_messenger, _SnapTracker_snaps, _SnapTracker_initialized, _SnapTracker_handleSnapAdded, _SnapTracker_handleSnapUnblocked, _SnapTracker_handleSnapRemoved;
|
|
13
13
|
import { projectLogger as log } from "./logger.mjs";
|
|
14
14
|
/**
|
|
15
15
|
* Checks if a given Snap is an account management Snap.
|
|
@@ -31,6 +31,7 @@ export class SnapTracker {
|
|
|
31
31
|
_SnapTracker_instances.add(this);
|
|
32
32
|
_SnapTracker_messenger.set(this, void 0);
|
|
33
33
|
_SnapTracker_snaps.set(this, new Set());
|
|
34
|
+
_SnapTracker_initialized.set(this, false);
|
|
34
35
|
__classPrivateFieldSet(this, _SnapTracker_messenger, messenger, "f");
|
|
35
36
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapInstalled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapAdded).call(this, snap, 'installed'));
|
|
36
37
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapUninstalled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snap.id, 'uninstalled'));
|
|
@@ -38,7 +39,27 @@ export class SnapTracker {
|
|
|
38
39
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapDisabled', (snap) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snap.id, 'disabled'));
|
|
39
40
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapBlocked', (snapId) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapRemoved).call(this, snapId, 'blocked'));
|
|
40
41
|
__classPrivateFieldGet(this, _SnapTracker_messenger, "f").subscribe('SnapController:snapUnblocked', (snapId) => __classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapUnblocked).call(this, snapId));
|
|
41
|
-
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Seeds the internal set of account-management Snaps from
|
|
45
|
+
* `SnapController:getRunnableSnaps`, then starts processing lifecycle
|
|
46
|
+
* events.
|
|
47
|
+
*/
|
|
48
|
+
async init() {
|
|
49
|
+
if (__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
50
|
+
// Do not re-init, once setup we only rely on lifecycle events to update the set of
|
|
51
|
+
// tracked Snaps.
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").clear();
|
|
55
|
+
const runnable = __classPrivateFieldGet(this, _SnapTracker_messenger, "f").call('SnapController:getRunnableSnaps');
|
|
56
|
+
for (const snap of runnable) {
|
|
57
|
+
if (isAccountManagementSnap(snap)) {
|
|
58
|
+
log(`Found account management Snap: ${snap.id} (initialization)`);
|
|
59
|
+
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
__classPrivateFieldSet(this, _SnapTracker_initialized, true, "f");
|
|
42
63
|
}
|
|
43
64
|
/**
|
|
44
65
|
* Returns the IDs of all currently tracked account-management Snaps.
|
|
@@ -58,15 +79,10 @@ export class SnapTracker {
|
|
|
58
79
|
return __classPrivateFieldGet(this, _SnapTracker_snaps, "f").has(snapId);
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
|
-
_SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _SnapTracker_instances = new WeakSet(),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (isAccountManagementSnap(snap)) {
|
|
65
|
-
log(`Found account management Snap: ${snap.id} (initialization)`);
|
|
66
|
-
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
67
|
-
}
|
|
82
|
+
_SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _SnapTracker_initialized = new WeakMap(), _SnapTracker_instances = new WeakSet(), _SnapTracker_handleSnapAdded = function _SnapTracker_handleSnapAdded(snap, reason) {
|
|
83
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
84
|
+
return;
|
|
68
85
|
}
|
|
69
|
-
}, _SnapTracker_handleSnapAdded = function _SnapTracker_handleSnapAdded(snap, reason) {
|
|
70
86
|
if (!snap.enabled || snap.blocked) {
|
|
71
87
|
return;
|
|
72
88
|
}
|
|
@@ -75,11 +91,17 @@ _SnapTracker_messenger = new WeakMap(), _SnapTracker_snaps = new WeakMap(), _Sna
|
|
|
75
91
|
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").add(snap.id);
|
|
76
92
|
}
|
|
77
93
|
}, _SnapTracker_handleSnapUnblocked = function _SnapTracker_handleSnapUnblocked(snapId) {
|
|
94
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
78
97
|
const snap = __classPrivateFieldGet(this, _SnapTracker_messenger, "f").call('SnapController:getSnap', snapId);
|
|
79
98
|
if (snap) {
|
|
80
99
|
__classPrivateFieldGet(this, _SnapTracker_instances, "m", _SnapTracker_handleSnapAdded).call(this, snap, 'unblocked');
|
|
81
100
|
}
|
|
82
101
|
}, _SnapTracker_handleSnapRemoved = function _SnapTracker_handleSnapRemoved(snapId, reason) {
|
|
102
|
+
if (!__classPrivateFieldGet(this, _SnapTracker_initialized, "f")) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
83
105
|
if (__classPrivateFieldGet(this, _SnapTracker_snaps, "f").has(snapId)) {
|
|
84
106
|
log(`Removed account management Snap: ${snapId} (${reason})`);
|
|
85
107
|
__classPrivateFieldGet(this, _SnapTracker_snaps, "f").delete(snapId);
|
package/dist/SnapTracker.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SnapTracker.mjs","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":";;;;;;;;;;;;AAGA,OAAO,EAAE,aAAa,IAAI,GAAG,EAAE,qBAAiB;AAGhD;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,IAAmB;IAClD,OAAO,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,KAAK,SAAS,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,WAAW;
|
|
1
|
+
{"version":3,"file":"SnapTracker.mjs","sourceRoot":"","sources":["../src/SnapTracker.ts"],"names":[],"mappings":"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","sourcesContent":["import { SnapId } from '@metamask/snaps-sdk';\nimport type { TruncatedSnap } from '@metamask/snaps-utils';\n\nimport { projectLogger as log } from './logger';\nimport type { SnapAccountServiceMessenger } from './SnapAccountService';\n\n/**\n * Checks if a given Snap is an account management Snap.\n *\n * @param snap - The Snap to check.\n * @returns True if the Snap declares the `endowment:keyring` initial\n * permission.\n */\nfunction isAccountManagementSnap(snap: TruncatedSnap): boolean {\n return snap.initialPermissions['endowment:keyring'] !== undefined;\n}\n\n/**\n * Tracks the set of installed, enabled, non-blocked account-management Snaps\n * (Snaps declaring the `endowment:keyring` initial permission) by listening to\n * `SnapController` lifecycle events.\n */\nexport class SnapTracker {\n readonly #messenger: SnapAccountServiceMessenger;\n\n readonly #snaps: Set<SnapId> = new Set();\n\n #initialized = false;\n\n constructor(messenger: SnapAccountServiceMessenger) {\n this.#messenger = messenger;\n\n this.#messenger.subscribe('SnapController:snapInstalled', (snap) =>\n this.#handleSnapAdded(snap, 'installed'),\n );\n this.#messenger.subscribe('SnapController:snapUninstalled', (snap) =>\n this.#handleSnapRemoved(snap.id, 'uninstalled'),\n );\n this.#messenger.subscribe('SnapController:snapEnabled', (snap) =>\n this.#handleSnapAdded(snap, 'enabled'),\n );\n this.#messenger.subscribe('SnapController:snapDisabled', (snap) =>\n this.#handleSnapRemoved(snap.id, 'disabled'),\n );\n this.#messenger.subscribe('SnapController:snapBlocked', (snapId) =>\n this.#handleSnapRemoved(snapId as SnapId, 'blocked'),\n );\n this.#messenger.subscribe('SnapController:snapUnblocked', (snapId) =>\n this.#handleSnapUnblocked(snapId as SnapId),\n );\n }\n\n /**\n * Seeds the internal set of account-management Snaps from\n * `SnapController:getRunnableSnaps`, then starts processing lifecycle\n * events.\n */\n async init(): Promise<void> {\n if (this.#initialized) {\n // Do not re-init, once setup we only rely on lifecycle events to update the set of\n // tracked Snaps.\n return;\n }\n\n this.#snaps.clear();\n\n const runnable = this.#messenger.call('SnapController:getRunnableSnaps');\n for (const snap of runnable) {\n if (isAccountManagementSnap(snap)) {\n log(`Found account management Snap: ${snap.id} (initialization)`);\n this.#snaps.add(snap.id);\n }\n }\n\n this.#initialized = true;\n }\n\n /**\n * Returns the IDs of all currently tracked account-management Snaps.\n *\n * @returns The IDs of tracked account-management Snaps.\n */\n getSnaps(): SnapId[] {\n return [...this.#snaps];\n }\n\n /**\n * Returns true if the given Snap ID is currently tracked and can be used.\n *\n * @param snapId - The Snap ID to check.\n * @returns True if the Snap is tracked and can be used.\n */\n canUse(snapId: SnapId): boolean {\n return this.#snaps.has(snapId);\n }\n\n /**\n * Handles a Snap being added (installed or enabled). If the Snap is an\n * account-management Snap, adds it to the internal set of tracked Snaps.\n *\n * @param snap - The Snap that was installed or enabled.\n * @param reason - The reason the Snap was added.\n */\n #handleSnapAdded(snap: TruncatedSnap, reason: string): void {\n if (!this.#initialized) {\n return;\n }\n\n if (!snap.enabled || snap.blocked) {\n return;\n }\n\n if (isAccountManagementSnap(snap) && !this.#snaps.has(snap.id)) {\n log(`Added account management Snap: ${snap.id} (${reason})`);\n\n this.#snaps.add(snap.id);\n }\n }\n\n /**\n * Handles a Snap being unblocked. If the Snap is an enabled\n * account-management Snap, re-adds it to the internal set of tracked Snaps.\n *\n * @param snapId - The Snap ID that was unblocked.\n */\n #handleSnapUnblocked(snapId: SnapId): void {\n if (!this.#initialized) {\n return;\n }\n\n const snap = this.#messenger.call('SnapController:getSnap', snapId);\n if (snap) {\n this.#handleSnapAdded(snap, 'unblocked');\n }\n }\n\n /**\n * Handles a Snap being removed (disabled, blocked, or uninstalled). If the Snap is an\n * account-management Snap, removes it from the internal set of tracked Snaps.\n *\n * @param snapId - The Snap ID that was disabled, blocked, or uninstalled.\n * @param reason - The reason the Snap was removed.\n */\n #handleSnapRemoved(snapId: SnapId, reason: string): void {\n if (!this.#initialized) {\n return;\n }\n\n if (this.#snaps.has(snapId)) {\n log(`Removed account management Snap: ${snapId} (${reason})`);\n\n this.#snaps.delete(snapId);\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/snap-account-service",
|
|
3
|
-
"version": "0.2.0-preview-
|
|
3
|
+
"version": "0.2.0-preview-e43dfcb",
|
|
4
4
|
"description": "Service for Account Management Snaps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -56,9 +56,7 @@
|
|
|
56
56
|
"@metamask/account-api": "^1.0.4",
|
|
57
57
|
"@metamask/account-tree-controller": "^7.4.0",
|
|
58
58
|
"@metamask/eth-snap-keyring": "^22.0.1",
|
|
59
|
-
"@metamask/keyring-api": "^23.1.0",
|
|
60
59
|
"@metamask/keyring-controller": "^25.5.0",
|
|
61
|
-
"@metamask/keyring-snap-sdk": "^9.0.1",
|
|
62
60
|
"@metamask/messenger": "^1.2.0",
|
|
63
61
|
"@metamask/snaps-controllers": "^19.0.0",
|
|
64
62
|
"@metamask/snaps-sdk": "^11.0.0",
|