@metamask-previews/eth-snap-keyring 4.3.2-672cc7b

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.
@@ -0,0 +1,599 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _SnapKeyring_instances, _SnapKeyring_snapClient, _SnapKeyring_accounts, _SnapKeyring_requests, _SnapKeyring_callbacks, _SnapKeyring_handleAccountCreated, _SnapKeyring_handleAccountUpdated, _SnapKeyring_handleAccountDeleted, _SnapKeyring_handleRequestApproved, _SnapKeyring_handleRequestRejected, _SnapKeyring_submitRequest, _SnapKeyring_hasMethod, _SnapKeyring_createRequestPromise, _SnapKeyring_submitSnapRequest, _SnapKeyring_handleSyncResponse, _SnapKeyring_handleAsyncResponse, _SnapKeyring_validateRedirectUrl, _SnapKeyring_resolveAddress, _SnapKeyring_getSnapMetadata, _SnapKeyring_getSnapAllowedOrigins;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SnapKeyring = exports.SNAP_KEYRING_TYPE = void 0;
16
+ const tx_1 = require("@ethereumjs/tx");
17
+ const eth_sig_util_1 = require("@metamask/eth-sig-util");
18
+ const keyring_internal_api_1 = require("@metamask/keyring-internal-api");
19
+ const keyring_snap_client_1 = require("@metamask/keyring-snap-client");
20
+ const superstruct_1 = require("@metamask/superstruct");
21
+ const utils_1 = require("@metamask/utils");
22
+ const events_1 = require("events");
23
+ const uuid_1 = require("uuid");
24
+ const DeferredPromise_1 = require("./DeferredPromise");
25
+ const logger_1 = require("./logger");
26
+ const SnapIdMap_1 = require("./SnapIdMap");
27
+ const types_1 = require("./types");
28
+ const util_1 = require("./util");
29
+ exports.SNAP_KEYRING_TYPE = 'Snap Keyring';
30
+ /**
31
+ * Keyring bridge implementation to support Snaps.
32
+ */
33
+ class SnapKeyring extends events_1.EventEmitter {
34
+ /**
35
+ * Create a new Snap keyring.
36
+ *
37
+ * @param controller - Snaps controller.
38
+ * @param callbacks - Callbacks used to interact with other components.
39
+ * @returns A new Snap keyring.
40
+ */
41
+ constructor(controller, callbacks) {
42
+ super();
43
+ _SnapKeyring_instances.add(this);
44
+ /**
45
+ * Client used to call the Snap keyring.
46
+ */
47
+ _SnapKeyring_snapClient.set(this, void 0);
48
+ /**
49
+ * Mapping between account IDs and an object that contains the associated
50
+ * account object and Snap ID.
51
+ */
52
+ _SnapKeyring_accounts.set(this, void 0);
53
+ /**
54
+ * Mapping between request IDs and their deferred promises.
55
+ */
56
+ _SnapKeyring_requests.set(this, void 0);
57
+ /**
58
+ * Callbacks used to interact with other components.
59
+ */
60
+ _SnapKeyring_callbacks.set(this, void 0);
61
+ this.type = SnapKeyring.type;
62
+ __classPrivateFieldSet(this, _SnapKeyring_snapClient, new keyring_snap_client_1.KeyringSnapControllerClient({ controller }), "f");
63
+ __classPrivateFieldSet(this, _SnapKeyring_requests, new SnapIdMap_1.SnapIdMap(), "f");
64
+ __classPrivateFieldSet(this, _SnapKeyring_accounts, new SnapIdMap_1.SnapIdMap(), "f");
65
+ __classPrivateFieldSet(this, _SnapKeyring_callbacks, callbacks, "f");
66
+ }
67
+ /**
68
+ * Handle a message from a Snap.
69
+ *
70
+ * @param snapId - ID of the Snap.
71
+ * @param message - Message sent by the Snap.
72
+ * @returns The execution result.
73
+ */
74
+ async handleKeyringSnapMessage(snapId, message) {
75
+ (0, superstruct_1.assert)(message, types_1.SnapMessageStruct);
76
+ switch (message.method) {
77
+ case keyring_internal_api_1.KeyringEvent.AccountCreated: {
78
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAccountCreated).call(this, snapId, message);
79
+ }
80
+ case keyring_internal_api_1.KeyringEvent.AccountUpdated: {
81
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAccountUpdated).call(this, snapId, message);
82
+ }
83
+ case keyring_internal_api_1.KeyringEvent.AccountDeleted: {
84
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAccountDeleted).call(this, snapId, message);
85
+ }
86
+ case keyring_internal_api_1.KeyringEvent.RequestApproved: {
87
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleRequestApproved).call(this, snapId, message);
88
+ }
89
+ case keyring_internal_api_1.KeyringEvent.RequestRejected: {
90
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleRequestRejected).call(this, snapId, message);
91
+ }
92
+ default:
93
+ throw new Error(`Method not supported: ${message.method}`);
94
+ }
95
+ }
96
+ /**
97
+ * Serialize the keyring state.
98
+ *
99
+ * @returns Serialized keyring state.
100
+ */
101
+ async serialize() {
102
+ return {
103
+ accounts: __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").toObject(),
104
+ };
105
+ }
106
+ /**
107
+ * Deserialize the keyring state into this keyring.
108
+ *
109
+ * @param state - Serialized keyring state.
110
+ */
111
+ async deserialize(state) {
112
+ // If the state is undefined, it means that this is a new keyring, so we
113
+ // don't need to do anything.
114
+ if (state === undefined) {
115
+ return;
116
+ }
117
+ __classPrivateFieldSet(this, _SnapKeyring_accounts, SnapIdMap_1.SnapIdMap.fromObject(state.accounts), "f");
118
+ }
119
+ /**
120
+ * Get the addresses of the accounts in this keyring.
121
+ *
122
+ * @returns The addresses of the accounts in this keyring.
123
+ */
124
+ async getAccounts() {
125
+ return (0, util_1.unique)([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].map(({ account }) => account.address.toLowerCase()));
126
+ }
127
+ /**
128
+ * Get the addresses of the accounts associated with a given Snap.
129
+ *
130
+ * @param snapId - Snap ID to filter by.
131
+ * @returns The addresses of the accounts associated with the given Snap.
132
+ */
133
+ async getAccountsBySnapId(snapId) {
134
+ return (0, util_1.unique)([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()]
135
+ .filter(({ snapId: accountSnapId }) => accountSnapId === snapId)
136
+ .map(({ account }) => account.address.toLowerCase()));
137
+ }
138
+ /**
139
+ * Sign a transaction.
140
+ *
141
+ * @param address - Sender's address.
142
+ * @param transaction - Transaction.
143
+ * @param _opts - Transaction options (not used).
144
+ */
145
+ async signTransaction(address, transaction, _opts = {}) {
146
+ const chainId = transaction.common.chainId();
147
+ const tx = (0, util_1.toJson)({
148
+ ...transaction.toJSON(),
149
+ from: address,
150
+ type: `0x${transaction.type.toString(16)}`,
151
+ chainId: (0, utils_1.bigIntToHex)(chainId),
152
+ });
153
+ const signedTx = await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
154
+ address,
155
+ method: keyring_internal_api_1.EthMethod.SignTransaction,
156
+ params: [tx],
157
+ chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
158
+ });
159
+ // ! It's *** CRITICAL *** that we mask the signature here, otherwise the
160
+ // ! Snap could overwrite the transaction.
161
+ const signature = (0, superstruct_1.mask)(signedTx, (0, superstruct_1.object)({
162
+ r: (0, superstruct_1.string)(),
163
+ s: (0, superstruct_1.string)(),
164
+ v: (0, superstruct_1.string)(),
165
+ }));
166
+ return tx_1.TransactionFactory.fromTxData({
167
+ ...tx,
168
+ r: signature.r,
169
+ s: signature.s,
170
+ v: signature.v,
171
+ });
172
+ }
173
+ /**
174
+ * Sign a typed data message.
175
+ *
176
+ * @param address - Signer's address.
177
+ * @param data - Data to sign.
178
+ * @param opts - Signing options.
179
+ * @returns The signature.
180
+ */
181
+ async signTypedData(address, data, opts = { version: eth_sig_util_1.SignTypedDataVersion.V1 }) {
182
+ const methods = {
183
+ [eth_sig_util_1.SignTypedDataVersion.V1]: keyring_internal_api_1.EthMethod.SignTypedDataV1,
184
+ [eth_sig_util_1.SignTypedDataVersion.V3]: keyring_internal_api_1.EthMethod.SignTypedDataV3,
185
+ [eth_sig_util_1.SignTypedDataVersion.V4]: keyring_internal_api_1.EthMethod.SignTypedDataV4,
186
+ };
187
+ // Use 'V1' by default to match other keyring implementations. V1 will be
188
+ // used if the version is not specified or not supported.
189
+ const method = methods[opts.version] || keyring_internal_api_1.EthMethod.SignTypedDataV1;
190
+ // Extract chain ID as if it was a typed message (as defined by EIP-712), if
191
+ // input is not a typed message, then chain ID will be undefined!
192
+ const chainId = data.domain?.chainId;
193
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
194
+ address,
195
+ method,
196
+ params: (0, util_1.toJson)([address, data]),
197
+ ...(chainId === undefined
198
+ ? {}
199
+ : {
200
+ chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
201
+ }),
202
+ }), keyring_internal_api_1.EthBytesStruct);
203
+ }
204
+ /**
205
+ * Sign a message.
206
+ *
207
+ * @param address - Signer's address.
208
+ * @param hash - Data to sign.
209
+ * @returns The signature.
210
+ */
211
+ async signMessage(address, hash) {
212
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
213
+ address,
214
+ method: keyring_internal_api_1.EthMethod.Sign,
215
+ params: (0, util_1.toJson)([address, hash]),
216
+ }), keyring_internal_api_1.EthBytesStruct);
217
+ }
218
+ /**
219
+ * Sign a personal message.
220
+ *
221
+ * Note: KeyringController says this should return a Buffer but it actually
222
+ * expects a string.
223
+ *
224
+ * @param address - Signer's address.
225
+ * @param data - Data to sign.
226
+ * @returns Promise of the signature.
227
+ */
228
+ async signPersonalMessage(address, data) {
229
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
230
+ address,
231
+ method: keyring_internal_api_1.EthMethod.PersonalSign,
232
+ params: (0, util_1.toJson)([data, address]),
233
+ }), keyring_internal_api_1.EthBytesStruct);
234
+ }
235
+ /**
236
+ * Convert a base transaction to a base UserOperation.
237
+ *
238
+ * @param address - Address of the sender.
239
+ * @param transactions - Base transactions to include in the UserOperation.
240
+ * @param context - Keyring execution context.
241
+ * @returns A pseudo-UserOperation that can be used to construct a real.
242
+ */
243
+ async prepareUserOperation(address, transactions, context) {
244
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
245
+ address,
246
+ method: keyring_internal_api_1.EthMethod.PrepareUserOperation,
247
+ params: (0, util_1.toJson)(transactions),
248
+ expectSync: true,
249
+ // We assume the chain ID is already well formatted
250
+ chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
251
+ }), keyring_internal_api_1.EthBaseUserOperationStruct);
252
+ }
253
+ /**
254
+ * Patches properties of a UserOperation. Currently, only the
255
+ * `paymasterAndData` can be patched.
256
+ *
257
+ * @param address - Address of the sender.
258
+ * @param userOp - UserOperation to patch.
259
+ * @param context - Keyring execution context.
260
+ * @returns A patch to apply to the UserOperation.
261
+ */
262
+ async patchUserOperation(address, userOp, context) {
263
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
264
+ address,
265
+ method: keyring_internal_api_1.EthMethod.PatchUserOperation,
266
+ params: (0, util_1.toJson)([userOp]),
267
+ expectSync: true,
268
+ // We assume the chain ID is already well formatted
269
+ chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
270
+ }), keyring_internal_api_1.EthUserOperationPatchStruct);
271
+ }
272
+ /**
273
+ * Signs an UserOperation.
274
+ *
275
+ * @param address - Address of the sender.
276
+ * @param userOp - UserOperation to sign.
277
+ * @param context - Leyring execution context.
278
+ * @returns The signature of the UserOperation.
279
+ */
280
+ async signUserOperation(address, userOp, context) {
281
+ return (0, util_1.strictMask)(await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitRequest).call(this, {
282
+ address,
283
+ method: keyring_internal_api_1.EthMethod.SignUserOperation,
284
+ params: (0, util_1.toJson)([userOp]),
285
+ // We assume the chain ID is already well formatted
286
+ chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
287
+ }), keyring_internal_api_1.EthBytesStruct);
288
+ }
289
+ /**
290
+ * Gets the private data associated with the given address so
291
+ * that it may be exported.
292
+ *
293
+ * If this keyring contains duplicate public keys the first
294
+ * matching address is exported.
295
+ *
296
+ * Used by the UI to export an account.
297
+ *
298
+ * @param _address - Address of the account to export.
299
+ */
300
+ exportAccount(_address) {
301
+ throw new Error('Exporting accounts from snaps is not supported.');
302
+ }
303
+ /**
304
+ * Removes the account matching the given address.
305
+ *
306
+ * @param address - Address of the account to remove.
307
+ */
308
+ async removeAccount(address) {
309
+ const { account, snapId } = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_resolveAddress).call(this, address);
310
+ // Always remove the account from the maps, even if the Snap is going to
311
+ // fail to delete it.
312
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").delete(snapId, account.id);
313
+ try {
314
+ await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).deleteAccount(account.id);
315
+ }
316
+ catch (error) {
317
+ // If the Snap failed to delete the account, log the error and continue
318
+ // with the account deletion, otherwise the account will be stuck in the
319
+ // keyring.
320
+ console.error(`Account '${address}' may not have been removed from snap '${snapId}':`, error);
321
+ }
322
+ }
323
+ /**
324
+ * Return an internal account object for a given address.
325
+ *
326
+ * @param address - Address of the account to return.
327
+ * @returns An internal account object for the given address.
328
+ */
329
+ getAccountByAddress(address) {
330
+ const accounts = this.listAccounts();
331
+ return accounts.find(({ address: accountAddress }) => (0, util_1.equalsIgnoreCase)(accountAddress, address));
332
+ }
333
+ /**
334
+ * List all Snap keyring accounts.
335
+ *
336
+ * @returns An array containing all Snap keyring accounts.
337
+ */
338
+ listAccounts() {
339
+ return [...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].map(({ account, snapId }) => {
340
+ const snap = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getSnapMetadata).call(this, snapId);
341
+ return {
342
+ ...account,
343
+ // TODO: Do not convert the address to lowercase.
344
+ //
345
+ // This is a workaround to support the current UI which expects the
346
+ // account address to be lowercase. This workaround should be removed
347
+ // once we migrated the UI to use the account ID instead of the account
348
+ // address.
349
+ address: account.address.toLowerCase(),
350
+ metadata: {
351
+ name: '',
352
+ importTime: 0,
353
+ keyring: {
354
+ type: this.type,
355
+ },
356
+ ...(snap !== undefined && { snap }),
357
+ },
358
+ };
359
+ });
360
+ }
361
+ }
362
+ exports.SnapKeyring = SnapKeyring;
363
+ _SnapKeyring_snapClient = new WeakMap(), _SnapKeyring_accounts = new WeakMap(), _SnapKeyring_requests = new WeakMap(), _SnapKeyring_callbacks = new WeakMap(), _SnapKeyring_instances = new WeakSet(), _SnapKeyring_handleAccountCreated =
364
+ /**
365
+ * Handle an Account Created event from a Snap.
366
+ *
367
+ * @param snapId - Snap ID.
368
+ * @param message - Event message.
369
+ * @returns `null`.
370
+ */
371
+ async function _SnapKeyring_handleAccountCreated(snapId, message) {
372
+ (0, superstruct_1.assert)(message, keyring_internal_api_1.AccountCreatedEventStruct);
373
+ const { account, accountNameSuggestion, displayConfirmation } = message.params;
374
+ // The UI still uses the account address to identify accounts, so we need
375
+ // to block the creation of duplicate accounts for now to prevent accounts
376
+ // from being overwritten.
377
+ if (await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").addressExists(account.address.toLowerCase())) {
378
+ throw new Error(`Account address '${account.address}' already exists`);
379
+ }
380
+ // A Snap could try to create an account with a different address but with
381
+ // an existing ID, so the above test only is not enough.
382
+ if (__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").has(snapId, account.id)) {
383
+ throw new Error(`Account '${account.id}' already exists`);
384
+ }
385
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").addAccount(account.address.toLowerCase(), snapId, async (accepted) => {
386
+ if (accepted) {
387
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(account.id, { account, snapId });
388
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
389
+ }
390
+ }, accountNameSuggestion, displayConfirmation);
391
+ return null;
392
+ }, _SnapKeyring_handleAccountUpdated =
393
+ /**
394
+ * Handle an Account Updated event from a Snap.
395
+ *
396
+ * @param snapId - Snap ID.
397
+ * @param message - Event message.
398
+ * @returns `null`.
399
+ */
400
+ async function _SnapKeyring_handleAccountUpdated(snapId, message) {
401
+ (0, superstruct_1.assert)(message, keyring_internal_api_1.AccountUpdatedEventStruct);
402
+ const { account: newAccount } = message.params;
403
+ const { account: oldAccount } = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").get(snapId, newAccount.id) ??
404
+ (0, util_1.throwError)(`Account '${newAccount.id}' not found`);
405
+ // The address of the account cannot be changed. In the future, we will
406
+ // support changing the address of an account since it will be required to
407
+ // support UTXO-based chains.
408
+ if (!(0, util_1.equalsIgnoreCase)(oldAccount.address, newAccount.address)) {
409
+ throw new Error(`Cannot change address of account '${newAccount.id}'`);
410
+ }
411
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(newAccount.id, { account: newAccount, snapId });
412
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
413
+ return null;
414
+ }, _SnapKeyring_handleAccountDeleted =
415
+ /**
416
+ * Handle an Account Deleted event from a Snap.
417
+ *
418
+ * @param snapId - Snap ID.
419
+ * @param message - Event message.
420
+ * @returns `null`.
421
+ */
422
+ async function _SnapKeyring_handleAccountDeleted(snapId, message) {
423
+ (0, superstruct_1.assert)(message, keyring_internal_api_1.AccountDeletedEventStruct);
424
+ const { id } = message.params;
425
+ const entry = __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").get(snapId, id);
426
+ // We can ignore the case where the account was already removed from the
427
+ // keyring, making the deletion idempotent.
428
+ //
429
+ // This happens when the keyring calls the Snap to delete an account, and
430
+ // the Snap calls the keyring back with an `AccountDeleted` event.
431
+ if (entry === undefined) {
432
+ return null;
433
+ }
434
+ // At this point we know that the account exists, so we can safely
435
+ // destructure it.
436
+ const { account: { address }, } = entry;
437
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").removeAccount(address.toLowerCase(), snapId, async (accepted) => {
438
+ if (accepted) {
439
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
440
+ }
441
+ });
442
+ return null;
443
+ }, _SnapKeyring_handleRequestApproved =
444
+ /**
445
+ * Handle an Request Approved event from a Snap.
446
+ *
447
+ * @param snapId - Snap ID.
448
+ * @param message - Event message.
449
+ * @returns `null`.
450
+ */
451
+ async function _SnapKeyring_handleRequestApproved(snapId, message) {
452
+ (0, superstruct_1.assert)(message, keyring_internal_api_1.RequestApprovedEventStruct);
453
+ const { id, result } = message.params;
454
+ const { promise } = __classPrivateFieldGet(this, _SnapKeyring_requests, "f").get(snapId, id) ?? (0, util_1.throwError)(`Request '${id}' not found`);
455
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, id);
456
+ promise.resolve(result);
457
+ return null;
458
+ }, _SnapKeyring_handleRequestRejected =
459
+ /**
460
+ * Handle an Request Rejected event from a Snap.
461
+ *
462
+ * @param snapId - Snap ID.
463
+ * @param message - Event message.
464
+ * @returns `null`.
465
+ */
466
+ async function _SnapKeyring_handleRequestRejected(snapId, message) {
467
+ (0, superstruct_1.assert)(message, keyring_internal_api_1.RequestRejectedEventStruct);
468
+ const { id } = message.params;
469
+ const { promise } = __classPrivateFieldGet(this, _SnapKeyring_requests, "f").get(snapId, id) ?? (0, util_1.throwError)(`Request '${id}' not found`);
470
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, id);
471
+ promise.reject(new Error(`Request rejected by user or snap.`));
472
+ return null;
473
+ }, _SnapKeyring_submitRequest =
474
+ /**
475
+ * Submit a request to a Snap.
476
+ *
477
+ * @param opts - Request options.
478
+ * @param opts.address - Account address.
479
+ * @param opts.method - Method to call.
480
+ * @param opts.params - Method parameters.
481
+ * @param opts.chainId - Selected chain ID (CAIP-2).
482
+ * @param opts.expectSync - Whether the request should be synchronous.
483
+ * @returns Promise that resolves to the result of the method call.
484
+ */
485
+ async function _SnapKeyring_submitRequest({ address, method, params, chainId = '', expectSync = false, }) {
486
+ const { account, snapId } = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_resolveAddress).call(this, address);
487
+ if (!__classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_hasMethod).call(this, account, method)) {
488
+ throw new Error(`Method '${method}' not supported for account ${account.address}`);
489
+ }
490
+ const requestId = (0, uuid_1.v4)();
491
+ // Create the promise before calling the Snap to prevent a race condition
492
+ // where the Snap responds before we have a chance to create it.
493
+ const promise = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_createRequestPromise).call(this, requestId, snapId);
494
+ const response = await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitSnapRequest).call(this, {
495
+ snapId,
496
+ requestId,
497
+ account,
498
+ method: method,
499
+ params,
500
+ chainId,
501
+ });
502
+ // Some methods, like the ones used to prepare and patch user operations,
503
+ // require the Snap to answer synchronously in order to work with the
504
+ // confirmation flow. This check lets the caller enforce this behavior.
505
+ if (expectSync && response.pending) {
506
+ throw new Error(`Request '${requestId}' to snap '${snapId}' is pending and expectSync is true.`);
507
+ }
508
+ // If the Snap answers synchronously, the promise must be removed from the
509
+ // map to prevent a leak.
510
+ if (!response.pending) {
511
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleSyncResponse).call(this, response, requestId, snapId);
512
+ }
513
+ // If the Snap answers asynchronously, we will inform the user with a redirect
514
+ if (response.redirect?.message || response.redirect?.url) {
515
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAsyncResponse).call(this, response.redirect, snapId);
516
+ }
517
+ return promise.promise;
518
+ }, _SnapKeyring_hasMethod = function _SnapKeyring_hasMethod(account, method) {
519
+ return account.methods.includes(method);
520
+ }, _SnapKeyring_createRequestPromise = function _SnapKeyring_createRequestPromise(requestId, snapId) {
521
+ const promise = new DeferredPromise_1.DeferredPromise();
522
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").set(requestId, { promise, snapId });
523
+ return promise;
524
+ }, _SnapKeyring_submitSnapRequest =
525
+ /**
526
+ * Submits a request to a Snap.
527
+ *
528
+ * @param options - The options for the Snap request.
529
+ * @param options.snapId - The Snap ID to submit the request to.
530
+ * @param options.requestId - The unique identifier for the request.
531
+ * @param options.account - The account to use for the request.
532
+ * @param options.method - The Ethereum method to call.
533
+ * @param options.params - The parameters to pass to the method, can be undefined.
534
+ * @param options.chainId - The chain ID to use for the request, can be an empty string.
535
+ * @returns A promise that resolves to the keyring response from the Snap.
536
+ * @throws An error if the Snap fails to respond or if there's an issue with the request submission.
537
+ */
538
+ async function _SnapKeyring_submitSnapRequest({ snapId, requestId, account, method, params, chainId, }) {
539
+ try {
540
+ const request = {
541
+ id: requestId,
542
+ scope: chainId,
543
+ account: account.id,
544
+ request: {
545
+ method,
546
+ ...(params !== undefined && { params }),
547
+ },
548
+ };
549
+ (0, logger_1.projectLogger)('Submit Snap request: ', request);
550
+ return await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).submitRequest(request);
551
+ }
552
+ catch (error) {
553
+ (0, logger_1.projectLogger)('Snap Request failed: ', { requestId });
554
+ // If the Snap failed to respond, delete the promise to prevent a leak.
555
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, requestId);
556
+ throw error;
557
+ }
558
+ }, _SnapKeyring_handleSyncResponse = function _SnapKeyring_handleSyncResponse(response, requestId, snapId) {
559
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, requestId);
560
+ return response.result;
561
+ }, _SnapKeyring_handleAsyncResponse =
562
+ /**
563
+ * Handles the async redirect and response from a Snap. Validates the redirect URL and informs the user with a message and URL if provided.
564
+ *
565
+ * @param redirect - The redirect information including message and URL.
566
+ * @param redirect.message - The message to show to the user if provided.
567
+ * @param redirect.url - The URL to redirect the user to if provided.
568
+ * @param snapId - The Snap ID associated with the request.
569
+ * @throws An error if the redirect URL is not an allowed origin for the Snap.
570
+ */
571
+ async function _SnapKeyring_handleAsyncResponse(redirect, snapId) {
572
+ const { message = '', url = '' } = redirect;
573
+ if (url) {
574
+ __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_validateRedirectUrl).call(this, url, snapId);
575
+ }
576
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").redirectUser(snapId, url, message);
577
+ }, _SnapKeyring_validateRedirectUrl = function _SnapKeyring_validateRedirectUrl(url, snapId) {
578
+ const { origin } = new URL(url);
579
+ const snap = __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").getController().get(snapId);
580
+ if (!snap) {
581
+ throw new Error(`Snap '${snapId}' not found.`);
582
+ }
583
+ const allowedOrigins = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getSnapAllowedOrigins).call(this, snap);
584
+ if (!allowedOrigins.includes(origin)) {
585
+ throw new Error(`Redirect URL domain '${origin}' is not an allowed origin by snap '${snapId}'`);
586
+ }
587
+ }, _SnapKeyring_resolveAddress = function _SnapKeyring_resolveAddress(address) {
588
+ return ([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].find(({ account }) => (0, util_1.equalsIgnoreCase)(account.address, address)) ?? (0, util_1.throwError)(`Account '${address}' not found`));
589
+ }, _SnapKeyring_getSnapMetadata = function _SnapKeyring_getSnapMetadata(snapId) {
590
+ const snap = __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").getController().get(snapId);
591
+ return snap
592
+ ? { id: snapId, name: snap.manifest.proposedName, enabled: snap.enabled }
593
+ : undefined;
594
+ }, _SnapKeyring_getSnapAllowedOrigins = function _SnapKeyring_getSnapAllowedOrigins(snap) {
595
+ return (snap.manifest.initialPermissions['endowment:keyring']?.allowedOrigins ??
596
+ []);
597
+ };
598
+ SnapKeyring.type = exports.SNAP_KEYRING_TYPE;
599
+ //# sourceMappingURL=SnapKeyring.js.map