@metamask-previews/eth-snap-keyring 19.0.0-fd40efd → 20.0.0-5c366be
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 +32 -1
- package/dist/SnapKeyring.cjs +169 -826
- package/dist/SnapKeyring.cjs.map +1 -1
- package/dist/SnapKeyring.d.cts +21 -11
- package/dist/SnapKeyring.d.cts.map +1 -1
- package/dist/SnapKeyring.d.mts +21 -11
- package/dist/SnapKeyring.d.mts.map +1 -1
- package/dist/SnapKeyring.mjs +169 -826
- package/dist/SnapKeyring.mjs.map +1 -1
- package/dist/SnapKeyringMessenger.cjs.map +1 -1
- package/dist/SnapKeyringMessenger.d.cts +2 -2
- package/dist/SnapKeyringMessenger.d.cts.map +1 -1
- package/dist/SnapKeyringMessenger.d.mts +2 -2
- package/dist/SnapKeyringMessenger.d.mts.map +1 -1
- package/dist/SnapKeyringMessenger.mjs.map +1 -1
- package/dist/SnapKeyringV1.cjs +736 -0
- package/dist/SnapKeyringV1.cjs.map +1 -0
- package/dist/SnapKeyringV1.d.cts +222 -0
- package/dist/SnapKeyringV1.d.cts.map +1 -0
- package/dist/SnapKeyringV1.d.mts +222 -0
- package/dist/SnapKeyringV1.d.mts.map +1 -0
- package/dist/SnapKeyringV1.mjs +732 -0
- package/dist/SnapKeyringV1.mjs.map +1 -0
- package/dist/SnapKeyringV2.cjs +375 -0
- package/dist/SnapKeyringV2.cjs.map +1 -0
- package/dist/SnapKeyringV2.d.cts +275 -0
- package/dist/SnapKeyringV2.d.cts.map +1 -0
- package/dist/SnapKeyringV2.d.mts +275 -0
- package/dist/SnapKeyringV2.d.mts.map +1 -0
- package/dist/SnapKeyringV2.mjs +371 -0
- package/dist/SnapKeyringV2.mjs.map +1 -0
- package/dist/events.d.cts +4 -4
- package/dist/events.d.mts +4 -4
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/util.cjs +16 -0
- package/dist/util.cjs.map +1 -1
- package/dist/util.d.cts +11 -0
- package/dist/util.d.cts.map +1 -1
- package/dist/util.d.mts +11 -0
- package/dist/util.d.mts.map +1 -1
- package/dist/util.mjs +15 -0
- package/dist/util.mjs.map +1 -1
- package/package.json +11 -10
- package/dist/DeferredPromise.cjs +0 -30
- package/dist/DeferredPromise.cjs.map +0 -1
- package/dist/DeferredPromise.d.cts +0 -16
- package/dist/DeferredPromise.d.cts.map +0 -1
- package/dist/DeferredPromise.d.mts +0 -16
- package/dist/DeferredPromise.d.mts.map +0 -1
- package/dist/DeferredPromise.mjs +0 -26
- package/dist/DeferredPromise.mjs.map +0 -1
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _SnapKeyringV1_instances, _SnapKeyringV1_callbacks, _SnapKeyringV1_isAnyAccountTypeAllowed, _SnapKeyringV1_requests, _SnapKeyringV1_options, _SnapKeyringV1_selectedAccounts, _SnapKeyringV1_handleAccountCreated, _SnapKeyringV1_handleAccountUpdated, _SnapKeyringV1_handleAccountDeleted, _SnapKeyringV1_handleRequestApproved, _SnapKeyringV1_handleRequestRejected, _SnapKeyringV1_handleGetSelectedAccounts, _SnapKeyringV1_handleAccountBalancesUpdated, _SnapKeyringV1_handleAccountAssetListUpdated, _SnapKeyringV1_handleAccountTransactionsUpdated, _SnapKeyringV1_rePublishAccountEvent, _SnapKeyringV1_createSnapAccount, _SnapKeyringV1_getInternalOptions, _SnapKeyringV1_getExistingAccount, _SnapKeyringV1_createRequestPromise, _SnapKeyringV1_handleAsyncRedirect, _SnapKeyringV1_validateRedirectUrl;
|
|
13
|
+
import { TransactionFactory } from "@ethereumjs/tx";
|
|
14
|
+
import { SignTypedDataVersion } from "@metamask/eth-sig-util";
|
|
15
|
+
import { AnyAccountType, EthMethod, KeyringEvent, EthBytesStruct, EthBaseUserOperationStruct, EthUserOperationPatchStruct, AccountBalancesUpdatedEventStruct, AccountAssetListUpdatedEventStruct, AccountTransactionsUpdatedEventStruct } from "@metamask/keyring-api";
|
|
16
|
+
import { toKeyringRequestWithoutOrigin } from "@metamask/keyring-internal-api";
|
|
17
|
+
import { KeyringInternalSnapClient } from "@metamask/keyring-internal-snap-client";
|
|
18
|
+
import { KeyringAccountRegistry } from "@metamask/keyring-sdk";
|
|
19
|
+
import { GetSelectedAccountsRequestStruct, SnapManageAccountsMethod } from "@metamask/keyring-snap-sdk";
|
|
20
|
+
import { strictMask } from "@metamask/keyring-utils";
|
|
21
|
+
import { assert, mask, object, string } from "@metamask/superstruct";
|
|
22
|
+
import { bigIntToHex, hasProperty, KnownCaipNamespace, toCaipChainId, createDeferredPromise } from "@metamask/utils";
|
|
23
|
+
import { v4 as uuid } from "uuid";
|
|
24
|
+
import { transformAccount } from "./account.mjs";
|
|
25
|
+
import { AccountCreatedEventStruct, AccountUpdatedEventStruct, AccountDeletedEventStruct, RequestApprovedEventStruct, RequestRejectedEventStruct } from "./events.mjs";
|
|
26
|
+
import { projectLogger as log } from "./logger.mjs";
|
|
27
|
+
import { getInternalOptionsOf } from "./options.mjs";
|
|
28
|
+
import { PLATFORM_VERSION_FOR_KEYRING_REQUEST_WITH_ORIGIN } from "./platform-versions.mjs";
|
|
29
|
+
import { SnapMessageStruct } from "./types.mjs";
|
|
30
|
+
import { equalsIgnoreCase, normalizeAccountAddress, sanitizeUrl, throwError, toJson } from "./util.mjs";
|
|
31
|
+
/**
|
|
32
|
+
* Per-snap coordinator for the v1 (event-driven) keyring flow.
|
|
33
|
+
*
|
|
34
|
+
* Each instance is scoped to exactly one `SnapId`. It handles all Snap
|
|
35
|
+
* keyring events (account lifecycle, request approval, asset updates) and
|
|
36
|
+
* owns the per-snap request / internal-options state.
|
|
37
|
+
*
|
|
38
|
+
* Designed to be subclassed: `SnapKeyringV2` extends this class and adds the
|
|
39
|
+
* `KeyringV2` interface on top, sharing the same registry and client.
|
|
40
|
+
*/
|
|
41
|
+
export class SnapKeyringV1 {
|
|
42
|
+
constructor({ snapId, messenger, callbacks, isAnyAccountTypeAllowed = false, }) {
|
|
43
|
+
_SnapKeyringV1_instances.add(this);
|
|
44
|
+
/** Injected callbacks for parent-level coordination. */
|
|
45
|
+
_SnapKeyringV1_callbacks.set(this, void 0);
|
|
46
|
+
/** Whether to allow the creation and update of generic accounts. */
|
|
47
|
+
_SnapKeyringV1_isAnyAccountTypeAllowed.set(this, void 0);
|
|
48
|
+
/**
|
|
49
|
+
* Pending async request promises, keyed by request ID.
|
|
50
|
+
* Plain Map is sufficient here since this instance is already snap-scoped.
|
|
51
|
+
*/
|
|
52
|
+
_SnapKeyringV1_requests.set(this, void 0);
|
|
53
|
+
/**
|
|
54
|
+
* Correlation ID → internal options, used to thread options from
|
|
55
|
+
* `createAccount` through to the `AccountCreated` event handler.
|
|
56
|
+
*/
|
|
57
|
+
_SnapKeyringV1_options.set(this, void 0);
|
|
58
|
+
/**
|
|
59
|
+
* The currently selected account IDs for this snap.
|
|
60
|
+
*/
|
|
61
|
+
_SnapKeyringV1_selectedAccounts.set(this, void 0);
|
|
62
|
+
this.snapId = snapId;
|
|
63
|
+
this.registry = new KeyringAccountRegistry();
|
|
64
|
+
this.messenger = messenger;
|
|
65
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_callbacks, callbacks, "f");
|
|
66
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_isAnyAccountTypeAllowed, isAnyAccountTypeAllowed, "f");
|
|
67
|
+
this.client = new KeyringInternalSnapClient({ messenger, snapId });
|
|
68
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_requests, new Map(), "f");
|
|
69
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_options, new Map(), "f");
|
|
70
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_selectedAccounts, [], "f");
|
|
71
|
+
}
|
|
72
|
+
// ──────────────────────────────────────────────
|
|
73
|
+
// Public API
|
|
74
|
+
// ──────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* Create a single account using the v1 event-driven flow.
|
|
77
|
+
*
|
|
78
|
+
* Handles the reserved 'metamask' field, correlation IDs, and internal
|
|
79
|
+
* options registration. The account is ultimately added to the registry
|
|
80
|
+
* via the `AccountCreated` event handler, not the return value.
|
|
81
|
+
*
|
|
82
|
+
* @param options - Account creation options.
|
|
83
|
+
* @param internalOptions - Internal Snap keyring options.
|
|
84
|
+
* @returns The account object returned by the snap.
|
|
85
|
+
*/
|
|
86
|
+
async createAccount(options, internalOptions) {
|
|
87
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_createSnapAccount).call(this, options, internalOptions);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Handle an incoming message from the Snap.
|
|
91
|
+
*
|
|
92
|
+
* Routes the message to the appropriate private handler based on the method.
|
|
93
|
+
*
|
|
94
|
+
* @param message - The message sent by the Snap.
|
|
95
|
+
* @returns The execution result.
|
|
96
|
+
*/
|
|
97
|
+
async handleKeyringSnapMessage(message) {
|
|
98
|
+
assert(message, SnapMessageStruct);
|
|
99
|
+
switch (message.method) {
|
|
100
|
+
case `${KeyringEvent.AccountCreated}`: {
|
|
101
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountCreated).call(this, message);
|
|
102
|
+
}
|
|
103
|
+
case `${KeyringEvent.AccountUpdated}`: {
|
|
104
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountUpdated).call(this, message);
|
|
105
|
+
}
|
|
106
|
+
case `${KeyringEvent.AccountDeleted}`: {
|
|
107
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountDeleted).call(this, message);
|
|
108
|
+
}
|
|
109
|
+
case `${KeyringEvent.RequestApproved}`: {
|
|
110
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleRequestApproved).call(this, message);
|
|
111
|
+
}
|
|
112
|
+
case `${KeyringEvent.RequestRejected}`: {
|
|
113
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleRequestRejected).call(this, message);
|
|
114
|
+
}
|
|
115
|
+
case `${KeyringEvent.AccountBalancesUpdated}`: {
|
|
116
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountBalancesUpdated).call(this, message);
|
|
117
|
+
}
|
|
118
|
+
case `${KeyringEvent.AccountAssetListUpdated}`: {
|
|
119
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountAssetListUpdated).call(this, message);
|
|
120
|
+
}
|
|
121
|
+
case `${KeyringEvent.AccountTransactionsUpdated}`: {
|
|
122
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAccountTransactionsUpdated).call(this, message);
|
|
123
|
+
}
|
|
124
|
+
case `${SnapManageAccountsMethod.GetSelectedAccounts}`: {
|
|
125
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleGetSelectedAccounts).call(this, message);
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
throw new Error(`Method not supported: ${message.method}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Submit a snap request for the given account.
|
|
133
|
+
*
|
|
134
|
+
* Manages the request promise lifecycle (creation, resolution, cleanup) and
|
|
135
|
+
* handles both synchronous and asynchronous (redirect) responses.
|
|
136
|
+
*
|
|
137
|
+
* @param options - The request options.
|
|
138
|
+
* @param options.origin - The sender origin.
|
|
139
|
+
* @param options.account - The account to submit the request for.
|
|
140
|
+
* @param options.method - The method to call.
|
|
141
|
+
* @param options.params - The method parameters.
|
|
142
|
+
* @param options.scope - The CAIP-2 chain ID.
|
|
143
|
+
* @param options.noPending - Whether the response is allowed to be pending.
|
|
144
|
+
* @returns A promise that resolves to the keyring response.
|
|
145
|
+
*/
|
|
146
|
+
async submitSnapRequest({ origin, account, method, params, scope, noPending, }) {
|
|
147
|
+
if (!account.methods.includes(method)) {
|
|
148
|
+
throw new Error(`Method '${method}' not supported for account ${account.address}`);
|
|
149
|
+
}
|
|
150
|
+
// Will both catch `undefined` and "empty" origins.
|
|
151
|
+
if (!origin?.trim()) {
|
|
152
|
+
throw new Error('An `origin` is required');
|
|
153
|
+
}
|
|
154
|
+
const requestId = uuid();
|
|
155
|
+
// Create the promise before calling the Snap to prevent a race condition
|
|
156
|
+
// where the Snap responds before we have a chance to create it.
|
|
157
|
+
const requestPromise = __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_createRequestPromise).call(this, requestId);
|
|
158
|
+
try {
|
|
159
|
+
const useOrigin = this.messenger.call('SnapController:isMinimumPlatformVersion', this.snapId, PLATFORM_VERSION_FOR_KEYRING_REQUEST_WITH_ORIGIN);
|
|
160
|
+
const request = {
|
|
161
|
+
id: requestId,
|
|
162
|
+
origin,
|
|
163
|
+
scope,
|
|
164
|
+
account: account.id,
|
|
165
|
+
request: {
|
|
166
|
+
method,
|
|
167
|
+
...(params !== undefined && { params }),
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
log('Submit Snap request: ', request);
|
|
171
|
+
let response;
|
|
172
|
+
if (useOrigin) {
|
|
173
|
+
response = await this.client.submitRequest(request);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
response = await this.client.submitRequestWithoutOrigin(toKeyringRequestWithoutOrigin(request));
|
|
177
|
+
}
|
|
178
|
+
if (noPending && response.pending) {
|
|
179
|
+
throw new Error(`Request '${requestId}' to Snap '${this.snapId}' is pending and noPending is true.`);
|
|
180
|
+
}
|
|
181
|
+
if (!response.pending) {
|
|
182
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").delete(requestId);
|
|
183
|
+
return response.result;
|
|
184
|
+
}
|
|
185
|
+
if (response.redirect?.message || response.redirect?.url) {
|
|
186
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_handleAsyncRedirect).call(this, response.redirect);
|
|
187
|
+
}
|
|
188
|
+
return requestPromise.promise;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
log('Snap Request failed: ', { requestId });
|
|
192
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").delete(requestId);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Update the selected accounts for this snap.
|
|
198
|
+
*
|
|
199
|
+
* @param accountIds - The account IDs to set as selected.
|
|
200
|
+
*/
|
|
201
|
+
async setSelectedAccounts(accountIds) {
|
|
202
|
+
__classPrivateFieldSet(this, _SnapKeyringV1_selectedAccounts, accountIds, "f");
|
|
203
|
+
try {
|
|
204
|
+
await this.client.setSelectedAccounts(accountIds);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
console.error(`Failed to set selected accounts for ${this.snapId} snap: '${error.message}'`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Resolve the account address associated with a signing request.
|
|
212
|
+
*
|
|
213
|
+
* Delegates directly to the snap-scoped client, which already has the
|
|
214
|
+
* snap ID baked in from construction time.
|
|
215
|
+
*
|
|
216
|
+
* @param scope - CAIP-2 chain ID.
|
|
217
|
+
* @param request - Signing request object.
|
|
218
|
+
* @returns The resolved address if found, `null` otherwise.
|
|
219
|
+
*/
|
|
220
|
+
async resolveAccountAddress(scope, request) {
|
|
221
|
+
return this.client.resolveAccountAddress(scope, request);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Sign a transaction for the given account.
|
|
225
|
+
*
|
|
226
|
+
* @param account - The account to sign the transaction for.
|
|
227
|
+
* @param transaction - The transaction to sign.
|
|
228
|
+
* @param _opts - Transaction options (unused).
|
|
229
|
+
* @returns A promise that resolves to the signed transaction.
|
|
230
|
+
*/
|
|
231
|
+
async signTransaction(account, transaction,
|
|
232
|
+
/* istanbul ignore next */
|
|
233
|
+
_opts = {}) {
|
|
234
|
+
const chainId = transaction.common.chainId();
|
|
235
|
+
const tx = toJson({
|
|
236
|
+
...transaction.toJSON(),
|
|
237
|
+
from: normalizeAccountAddress(account),
|
|
238
|
+
type: `0x${transaction.type.toString(16)}`,
|
|
239
|
+
chainId: bigIntToHex(chainId),
|
|
240
|
+
});
|
|
241
|
+
const signedTx = await this.submitSnapRequest({
|
|
242
|
+
origin: 'metamask',
|
|
243
|
+
account,
|
|
244
|
+
method: EthMethod.SignTransaction,
|
|
245
|
+
params: [tx],
|
|
246
|
+
scope: toCaipChainId(KnownCaipNamespace.Eip155, `${chainId}`),
|
|
247
|
+
noPending: false,
|
|
248
|
+
});
|
|
249
|
+
// ! It's *** CRITICAL *** that we mask the signature here, otherwise the
|
|
250
|
+
// ! Snap could overwrite the transaction.
|
|
251
|
+
const signature = mask(signedTx, object({
|
|
252
|
+
r: string(),
|
|
253
|
+
s: string(),
|
|
254
|
+
v: string(),
|
|
255
|
+
}));
|
|
256
|
+
return TransactionFactory.fromTxData({
|
|
257
|
+
...tx,
|
|
258
|
+
r: signature.r,
|
|
259
|
+
s: signature.s,
|
|
260
|
+
v: signature.v,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Sign a typed data message for the given account.
|
|
265
|
+
*
|
|
266
|
+
* @param account - The account to sign the typed data for.
|
|
267
|
+
* @param data - The typed data to sign.
|
|
268
|
+
* @param opts - Signing options.
|
|
269
|
+
* @param opts.version - The typed data version to use.
|
|
270
|
+
* @returns A promise that resolves to the signature.
|
|
271
|
+
*/
|
|
272
|
+
async signTypedData(account, data, opts) {
|
|
273
|
+
const methods = {
|
|
274
|
+
[SignTypedDataVersion.V1]: EthMethod.SignTypedDataV1,
|
|
275
|
+
[SignTypedDataVersion.V3]: EthMethod.SignTypedDataV3,
|
|
276
|
+
[SignTypedDataVersion.V4]: EthMethod.SignTypedDataV4,
|
|
277
|
+
};
|
|
278
|
+
// Use 'V1' by default to match other keyring implementations. V1 will be
|
|
279
|
+
// used if the version is not specified or not supported.
|
|
280
|
+
const method = methods[opts.version] || EthMethod.SignTypedDataV1;
|
|
281
|
+
// Extract chain ID as if it was a typed message (as defined by EIP-712), if
|
|
282
|
+
// input is not a typed message, then chain ID will be undefined!
|
|
283
|
+
const chainId = data.domain?.chainId;
|
|
284
|
+
const address = normalizeAccountAddress(account);
|
|
285
|
+
return strictMask(await this.submitSnapRequest({
|
|
286
|
+
origin: 'metamask',
|
|
287
|
+
account,
|
|
288
|
+
method,
|
|
289
|
+
params: toJson([address, data]),
|
|
290
|
+
scope: chainId === undefined
|
|
291
|
+
? ''
|
|
292
|
+
: toCaipChainId(KnownCaipNamespace.Eip155, `${chainId}`),
|
|
293
|
+
noPending: false,
|
|
294
|
+
}), EthBytesStruct);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Sign a message (eth_sign) for the given account.
|
|
298
|
+
*
|
|
299
|
+
* @param account - The account to sign the message for.
|
|
300
|
+
* @param hash - The data to sign.
|
|
301
|
+
* @returns A promise that resolves to the signature.
|
|
302
|
+
*/
|
|
303
|
+
async signMessage(account, hash) {
|
|
304
|
+
const address = normalizeAccountAddress(account);
|
|
305
|
+
return strictMask(await this.submitSnapRequest({
|
|
306
|
+
origin: 'metamask',
|
|
307
|
+
account,
|
|
308
|
+
method: EthMethod.Sign,
|
|
309
|
+
params: toJson([address, hash]),
|
|
310
|
+
scope: '',
|
|
311
|
+
noPending: false,
|
|
312
|
+
}), EthBytesStruct);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Sign a personal message for the given account.
|
|
316
|
+
*
|
|
317
|
+
* Note: KeyringController says this should return a Buffer but it actually
|
|
318
|
+
* expects a string.
|
|
319
|
+
*
|
|
320
|
+
* @param account - The account to sign the message for.
|
|
321
|
+
* @param data - The data to sign.
|
|
322
|
+
* @returns A promise that resolves to the signature.
|
|
323
|
+
*/
|
|
324
|
+
async signPersonalMessage(account, data) {
|
|
325
|
+
const address = normalizeAccountAddress(account);
|
|
326
|
+
return strictMask(await this.submitSnapRequest({
|
|
327
|
+
origin: 'metamask',
|
|
328
|
+
account,
|
|
329
|
+
method: EthMethod.PersonalSign,
|
|
330
|
+
params: toJson([data, address]),
|
|
331
|
+
scope: '',
|
|
332
|
+
noPending: false,
|
|
333
|
+
}), EthBytesStruct);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Convert a base transaction to a base UserOperation.
|
|
337
|
+
*
|
|
338
|
+
* @param account - The account to prepare the user operation for.
|
|
339
|
+
* @param transactions - Base transactions to include in the UserOperation.
|
|
340
|
+
* @param context - Keyring execution context.
|
|
341
|
+
* @returns A pseudo-UserOperation that can be used to construct a real one.
|
|
342
|
+
*/
|
|
343
|
+
async prepareUserOperation(account, transactions, context) {
|
|
344
|
+
return strictMask(await this.submitSnapRequest({
|
|
345
|
+
origin: 'metamask',
|
|
346
|
+
account,
|
|
347
|
+
method: EthMethod.PrepareUserOperation,
|
|
348
|
+
params: toJson(transactions),
|
|
349
|
+
noPending: true,
|
|
350
|
+
scope: toCaipChainId(KnownCaipNamespace.Eip155, context.chainId),
|
|
351
|
+
}), EthBaseUserOperationStruct);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Patches properties of a UserOperation. Currently, only the
|
|
355
|
+
* `paymasterAndData` can be patched.
|
|
356
|
+
*
|
|
357
|
+
* @param account - The account to patch the user operation for.
|
|
358
|
+
* @param userOp - UserOperation to patch.
|
|
359
|
+
* @param context - Keyring execution context.
|
|
360
|
+
* @returns A patch to apply to the UserOperation.
|
|
361
|
+
*/
|
|
362
|
+
async patchUserOperation(account, userOp, context) {
|
|
363
|
+
return strictMask(await this.submitSnapRequest({
|
|
364
|
+
origin: 'metamask',
|
|
365
|
+
account,
|
|
366
|
+
method: EthMethod.PatchUserOperation,
|
|
367
|
+
params: toJson([userOp]),
|
|
368
|
+
noPending: true,
|
|
369
|
+
scope: toCaipChainId(KnownCaipNamespace.Eip155, context.chainId),
|
|
370
|
+
}), EthUserOperationPatchStruct);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Signs a UserOperation.
|
|
374
|
+
*
|
|
375
|
+
* @param account - The account to sign the user operation for.
|
|
376
|
+
* @param userOp - UserOperation to sign.
|
|
377
|
+
* @param context - Keyring execution context.
|
|
378
|
+
* @returns A promise that resolves to the signature.
|
|
379
|
+
*/
|
|
380
|
+
async signUserOperation(account, userOp, context) {
|
|
381
|
+
return strictMask(await this.submitSnapRequest({
|
|
382
|
+
origin: 'metamask',
|
|
383
|
+
account,
|
|
384
|
+
method: EthMethod.SignUserOperation,
|
|
385
|
+
params: toJson([userOp]),
|
|
386
|
+
noPending: false,
|
|
387
|
+
scope: toCaipChainId(KnownCaipNamespace.Eip155, context.chainId),
|
|
388
|
+
}), EthBytesStruct);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
_SnapKeyringV1_callbacks = new WeakMap(), _SnapKeyringV1_isAnyAccountTypeAllowed = new WeakMap(), _SnapKeyringV1_requests = new WeakMap(), _SnapKeyringV1_options = new WeakMap(), _SnapKeyringV1_selectedAccounts = new WeakMap(), _SnapKeyringV1_instances = new WeakSet(), _SnapKeyringV1_handleAccountCreated =
|
|
392
|
+
// ──────────────────────────────────────────────
|
|
393
|
+
// Private handlers
|
|
394
|
+
// ──────────────────────────────────────────────
|
|
395
|
+
/**
|
|
396
|
+
* Handle an Account Created event from the Snap.
|
|
397
|
+
*
|
|
398
|
+
* @param message - Event message.
|
|
399
|
+
* @returns `null`.
|
|
400
|
+
*/
|
|
401
|
+
async function _SnapKeyringV1_handleAccountCreated(message) {
|
|
402
|
+
assert(message, AccountCreatedEventStruct);
|
|
403
|
+
const { metamask, account: newAccountFromEvent, accountNameSuggestion, displayAccountNameSuggestion, displayConfirmation, } = message.params;
|
|
404
|
+
// READ THIS CAREFULLY:
|
|
405
|
+
// ------------------------------------------------------------------------
|
|
406
|
+
// The account creation flow is now asynchronous. We expect the Snap to
|
|
407
|
+
// first create the account data and then fire the "AccountCreated" event.
|
|
408
|
+
// Potentially migrate the account.
|
|
409
|
+
const account = transformAccount(newAccountFromEvent);
|
|
410
|
+
const address = normalizeAccountAddress(account);
|
|
411
|
+
if (__classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_getExistingAccount).call(this, account)) {
|
|
412
|
+
// If the account already exists, we skip it.
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
// Make sure this new account is valid.
|
|
416
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").assertAccountCanBeUsed(account);
|
|
417
|
+
// A deferred promise that will be resolved once the Snap keyring has saved
|
|
418
|
+
// its internal state.
|
|
419
|
+
// This part of the flow is run asynchronously, so we have no other way of
|
|
420
|
+
// letting the MetaMask client that this "save" has been run.
|
|
421
|
+
// NOTE: Another way of fixing that could be to rely on events through the
|
|
422
|
+
// messenger maybe?
|
|
423
|
+
const onceSaved = createDeferredPromise();
|
|
424
|
+
// Add the account to the keyring, but wait for the MetaMask client to
|
|
425
|
+
// approve the account creation first.
|
|
426
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").addAccount(address,
|
|
427
|
+
// This callback is passed to the MetaMask client, it will be called whenever
|
|
428
|
+
// the end user will accept or not the account creation.
|
|
429
|
+
async (accepted) => {
|
|
430
|
+
if (accepted) {
|
|
431
|
+
// We consider the account to be created on the Snap keyring only if
|
|
432
|
+
// the user accepted it. Meaning that the Snap MIGHT HAVE created the
|
|
433
|
+
// account on its own state, but the Snap keyring MIGHT NOT HAVE it yet.
|
|
434
|
+
//
|
|
435
|
+
// e.g The account creation dialog crashed on MetaMask, this callback
|
|
436
|
+
// will never be called, but the Snap still has the account.
|
|
437
|
+
this.registry.set(account);
|
|
438
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").onRegister?.(account.id);
|
|
439
|
+
// This is the "true async part". We do not `await` for this call, mainly
|
|
440
|
+
// because this callback will persist the account on the client side
|
|
441
|
+
// (through the `AccountsController`).
|
|
442
|
+
//
|
|
443
|
+
// Since this will happen after the Snap account creation and Snap
|
|
444
|
+
// event, if anything goes wrong, we will delete the account by
|
|
445
|
+
// calling `deleteAccount` on the Snap.
|
|
446
|
+
// eslint-disable-next-line no-void
|
|
447
|
+
void __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f")
|
|
448
|
+
.saveState()
|
|
449
|
+
.then(() => {
|
|
450
|
+
// This allows the MetaMask client to be "notified" when then
|
|
451
|
+
// Snap keyring has truly persisted its state. From there, we should
|
|
452
|
+
// be able to use the account (e.g. to display account creation
|
|
453
|
+
// confirmation dialogs).
|
|
454
|
+
onceSaved.resolve(account.id);
|
|
455
|
+
})
|
|
456
|
+
.catch(
|
|
457
|
+
/* istanbul ignore next */
|
|
458
|
+
async (error) => {
|
|
459
|
+
// FIXME: There's a potential race condition here, if the Snap did
|
|
460
|
+
// not persist the account yet (this should mostly be for older Snaps).
|
|
461
|
+
this.registry.delete(account.id);
|
|
462
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").onUnregister?.(account.id);
|
|
463
|
+
await this.client
|
|
464
|
+
.deleteAccount(account.id)
|
|
465
|
+
/* istanbul ignore next */
|
|
466
|
+
.catch(() => {
|
|
467
|
+
// Swallow — best-effort cleanup.
|
|
468
|
+
});
|
|
469
|
+
// This allows the MetaMask client to be "notified" that something went
|
|
470
|
+
// wrong with the Snap keyring. (e.g. useful to display account creation
|
|
471
|
+
// error dialogs).
|
|
472
|
+
onceSaved.reject(error);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}, onceSaved.promise, accountNameSuggestion, getInternalOptionsOf([
|
|
476
|
+
// 1. We use the internal options from the Snap keyring.
|
|
477
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_getInternalOptions).call(this, metamask?.correlationId) ?? {},
|
|
478
|
+
// 2. We use the ones coming from the Snap.
|
|
479
|
+
{
|
|
480
|
+
displayConfirmation,
|
|
481
|
+
displayAccountNameSuggestion,
|
|
482
|
+
},
|
|
483
|
+
]));
|
|
484
|
+
return null;
|
|
485
|
+
}, _SnapKeyringV1_handleAccountUpdated =
|
|
486
|
+
/**
|
|
487
|
+
* Handle an Account Updated event from the Snap.
|
|
488
|
+
*
|
|
489
|
+
* @param message - Event message.
|
|
490
|
+
* @returns `null`.
|
|
491
|
+
*/
|
|
492
|
+
async function _SnapKeyringV1_handleAccountUpdated(message) {
|
|
493
|
+
assert(message, AccountUpdatedEventStruct);
|
|
494
|
+
const { account: newAccountFromEvent } = message.params;
|
|
495
|
+
const oldAccount = this.registry.get(newAccountFromEvent.id) ??
|
|
496
|
+
throwError(`Account '${newAccountFromEvent.id}' not found`);
|
|
497
|
+
// Potentially migrate the account.
|
|
498
|
+
const newAccount = transformAccount(newAccountFromEvent);
|
|
499
|
+
// The `AnyAccountType.Account` generic account type is allowed only during
|
|
500
|
+
// development, so we check whether it's allowed before continuing.
|
|
501
|
+
//
|
|
502
|
+
// An account cannot be updated if the `isAnyAccountTypeAllowed` flag is
|
|
503
|
+
// set to `false` and the new or old account is a generic account.
|
|
504
|
+
const isGenericAccountInvolved = newAccount.type === AnyAccountType.Account ||
|
|
505
|
+
oldAccount.type === AnyAccountType.Account;
|
|
506
|
+
if (!__classPrivateFieldGet(this, _SnapKeyringV1_isAnyAccountTypeAllowed, "f") && isGenericAccountInvolved) {
|
|
507
|
+
throw new Error(`Cannot update generic account '${newAccount.id}'`);
|
|
508
|
+
}
|
|
509
|
+
// The address of the account cannot be changed. In the future, we will
|
|
510
|
+
// support changing the address of an account since it will be required to
|
|
511
|
+
// support UTXO-based chains.
|
|
512
|
+
if (!equalsIgnoreCase(oldAccount.address, newAccount.address)) {
|
|
513
|
+
throw new Error(`Cannot change address of account '${newAccount.id}'`);
|
|
514
|
+
}
|
|
515
|
+
this.registry.set(newAccount);
|
|
516
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").saveState();
|
|
517
|
+
return null;
|
|
518
|
+
}, _SnapKeyringV1_handleAccountDeleted =
|
|
519
|
+
/**
|
|
520
|
+
* Handle an Account Deleted event from the Snap.
|
|
521
|
+
*
|
|
522
|
+
* @param message - Event message.
|
|
523
|
+
* @returns `null`.
|
|
524
|
+
*/
|
|
525
|
+
async function _SnapKeyringV1_handleAccountDeleted(message) {
|
|
526
|
+
assert(message, AccountDeletedEventStruct);
|
|
527
|
+
const { id } = message.params;
|
|
528
|
+
const account = this.registry.get(id);
|
|
529
|
+
// We can ignore the case where the account was already removed from the
|
|
530
|
+
// keyring, making the deletion idempotent.
|
|
531
|
+
//
|
|
532
|
+
// This happens when the keyring calls the Snap to delete an account, and
|
|
533
|
+
// the Snap calls the keyring back with an `AccountDeleted` event.
|
|
534
|
+
if (account === undefined) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").removeAccount(normalizeAccountAddress(account), async (accepted) => {
|
|
538
|
+
if (accepted) {
|
|
539
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").saveState();
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
return null;
|
|
543
|
+
}, _SnapKeyringV1_handleRequestApproved =
|
|
544
|
+
/**
|
|
545
|
+
* Handle a Request Approved event from the Snap.
|
|
546
|
+
*
|
|
547
|
+
* @param message - Event message.
|
|
548
|
+
* @returns `null`.
|
|
549
|
+
*/
|
|
550
|
+
async function _SnapKeyringV1_handleRequestApproved(message) {
|
|
551
|
+
assert(message, RequestApprovedEventStruct);
|
|
552
|
+
const { id, result } = message.params;
|
|
553
|
+
const promise = __classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").get(id) ?? throwError(`Request '${id}' not found`);
|
|
554
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").delete(id);
|
|
555
|
+
promise.resolve(result);
|
|
556
|
+
return null;
|
|
557
|
+
}, _SnapKeyringV1_handleRequestRejected =
|
|
558
|
+
/**
|
|
559
|
+
* Handle a Request Rejected event from the Snap.
|
|
560
|
+
*
|
|
561
|
+
* @param message - Event message.
|
|
562
|
+
* @returns `null`.
|
|
563
|
+
*/
|
|
564
|
+
async function _SnapKeyringV1_handleRequestRejected(message) {
|
|
565
|
+
assert(message, RequestRejectedEventStruct);
|
|
566
|
+
const { id } = message.params;
|
|
567
|
+
const promise = __classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").get(id) ?? throwError(`Request '${id}' not found`);
|
|
568
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").delete(id);
|
|
569
|
+
promise.reject(new Error(`Request rejected by user or snap.`));
|
|
570
|
+
return null;
|
|
571
|
+
}, _SnapKeyringV1_handleGetSelectedAccounts =
|
|
572
|
+
/**
|
|
573
|
+
* Handle a Get Selected Accounts method call from the Snap.
|
|
574
|
+
*
|
|
575
|
+
* @param message - Method call message.
|
|
576
|
+
* @returns The selected accounts.
|
|
577
|
+
*/
|
|
578
|
+
async function _SnapKeyringV1_handleGetSelectedAccounts(message) {
|
|
579
|
+
assert(message, GetSelectedAccountsRequestStruct);
|
|
580
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_selectedAccounts, "f");
|
|
581
|
+
}, _SnapKeyringV1_handleAccountBalancesUpdated =
|
|
582
|
+
/**
|
|
583
|
+
* Handle an Account Balances Updated event from the Snap.
|
|
584
|
+
*
|
|
585
|
+
* @param message - Event message.
|
|
586
|
+
* @returns `null`.
|
|
587
|
+
*/
|
|
588
|
+
async function _SnapKeyringV1_handleAccountBalancesUpdated(message) {
|
|
589
|
+
assert(message, AccountBalancesUpdatedEventStruct);
|
|
590
|
+
const event = message.params;
|
|
591
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_rePublishAccountEvent).call(this, 'SnapKeyring:accountBalancesUpdated', (filter) => {
|
|
592
|
+
event.balances = filter(event.balances);
|
|
593
|
+
return [event];
|
|
594
|
+
});
|
|
595
|
+
}, _SnapKeyringV1_handleAccountAssetListUpdated =
|
|
596
|
+
/**
|
|
597
|
+
* Handle an Account Asset List Updated event from the Snap.
|
|
598
|
+
*
|
|
599
|
+
* @param message - Event message.
|
|
600
|
+
* @returns `null`.
|
|
601
|
+
*/
|
|
602
|
+
async function _SnapKeyringV1_handleAccountAssetListUpdated(message) {
|
|
603
|
+
assert(message, AccountAssetListUpdatedEventStruct);
|
|
604
|
+
const event = message.params;
|
|
605
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_rePublishAccountEvent).call(this, 'SnapKeyring:accountAssetListUpdated', (filter) => {
|
|
606
|
+
event.assets = filter(event.assets);
|
|
607
|
+
return [event];
|
|
608
|
+
});
|
|
609
|
+
}, _SnapKeyringV1_handleAccountTransactionsUpdated =
|
|
610
|
+
/**
|
|
611
|
+
* Handle an Account Transactions Updated event from the Snap.
|
|
612
|
+
*
|
|
613
|
+
* @param message - Event message.
|
|
614
|
+
* @returns `null`.
|
|
615
|
+
*/
|
|
616
|
+
async function _SnapKeyringV1_handleAccountTransactionsUpdated(message) {
|
|
617
|
+
assert(message, AccountTransactionsUpdatedEventStruct);
|
|
618
|
+
const event = message.params;
|
|
619
|
+
return __classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_rePublishAccountEvent).call(this, 'SnapKeyring:accountTransactionsUpdated', (filter) => {
|
|
620
|
+
event.transactions = filter(event.transactions);
|
|
621
|
+
return [event];
|
|
622
|
+
});
|
|
623
|
+
}, _SnapKeyringV1_rePublishAccountEvent =
|
|
624
|
+
// ──────────────────────────────────────────────
|
|
625
|
+
// Private helpers
|
|
626
|
+
// ──────────────────────────────────────────────
|
|
627
|
+
/**
|
|
628
|
+
* Re-publish an account event after filtering out accounts that don't
|
|
629
|
+
* belong to this snap.
|
|
630
|
+
*
|
|
631
|
+
* Uses the registry as the source of truth: only accounts present in
|
|
632
|
+
* this snap's registry are forwarded. This prevents a snap from emitting
|
|
633
|
+
* updates for accounts it doesn't own.
|
|
634
|
+
*
|
|
635
|
+
* @param event - The event type to re-publish.
|
|
636
|
+
* @param filteredEventCallback - Callback that receives the filter function
|
|
637
|
+
* and returns the event payload to publish.
|
|
638
|
+
* @template EventType - A Snap keyring event type.
|
|
639
|
+
* @returns `null`.
|
|
640
|
+
*/
|
|
641
|
+
async function _SnapKeyringV1_rePublishAccountEvent(event, filteredEventCallback) {
|
|
642
|
+
const filter = (accountMapping) => {
|
|
643
|
+
return Object.entries(accountMapping).reduce((filtered, [accountId, entry]) => {
|
|
644
|
+
if (this.registry.has(accountId)) {
|
|
645
|
+
filtered[accountId] = entry;
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
console.warn(`SnapKeyring - ${event} - Found an unknown account ID "${accountId}" for Snap ID "${this.snapId}". Skipping.`);
|
|
649
|
+
}
|
|
650
|
+
return filtered;
|
|
651
|
+
}, {});
|
|
652
|
+
};
|
|
653
|
+
this.messenger.publish(event, ...filteredEventCallback(filter));
|
|
654
|
+
return null;
|
|
655
|
+
}, _SnapKeyringV1_createSnapAccount =
|
|
656
|
+
/**
|
|
657
|
+
* Core logic for the v1 `createAccount` flow.
|
|
658
|
+
*
|
|
659
|
+
* Handles the reserved 'metamask' field, correlation IDs, and internal
|
|
660
|
+
* options registration. The account is ultimately added to the registry
|
|
661
|
+
* via the `AccountCreated` event handler, not the return value.
|
|
662
|
+
*
|
|
663
|
+
* @param options - Account creation options.
|
|
664
|
+
* @param internalOptions - Internal Snap keyring options.
|
|
665
|
+
* @returns The account object returned by the snap.
|
|
666
|
+
*/
|
|
667
|
+
async function _SnapKeyringV1_createSnapAccount(options, internalOptions) {
|
|
668
|
+
const reserved = 'metamask';
|
|
669
|
+
if (hasProperty(options, reserved)) {
|
|
670
|
+
throw new Error(`The '${reserved}' property is reserved for internal use`);
|
|
671
|
+
}
|
|
672
|
+
if (!internalOptions) {
|
|
673
|
+
return await this.client.createAccount(options);
|
|
674
|
+
}
|
|
675
|
+
const correlationId = uuid();
|
|
676
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_options, "f").set(correlationId, internalOptions);
|
|
677
|
+
return await this.client.createAccount({
|
|
678
|
+
...options,
|
|
679
|
+
metamask: {
|
|
680
|
+
correlationId,
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
}, _SnapKeyringV1_getInternalOptions = function _SnapKeyringV1_getInternalOptions(correlationId) {
|
|
684
|
+
if (!correlationId) {
|
|
685
|
+
return undefined;
|
|
686
|
+
}
|
|
687
|
+
const options = __classPrivateFieldGet(this, _SnapKeyringV1_options, "f").get(correlationId);
|
|
688
|
+
if (options) {
|
|
689
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_options, "f").delete(correlationId);
|
|
690
|
+
return options;
|
|
691
|
+
}
|
|
692
|
+
console.warn(`SnapKeyring - Received unmapped correlation ID: "${correlationId}"`);
|
|
693
|
+
return undefined;
|
|
694
|
+
}, _SnapKeyringV1_getExistingAccount = function _SnapKeyringV1_getExistingAccount(account) {
|
|
695
|
+
const address = normalizeAccountAddress(account);
|
|
696
|
+
const existing = this.registry.get(account.id);
|
|
697
|
+
if (existing && normalizeAccountAddress(existing) === address) {
|
|
698
|
+
return existing;
|
|
699
|
+
}
|
|
700
|
+
return undefined;
|
|
701
|
+
}, _SnapKeyringV1_createRequestPromise = function _SnapKeyringV1_createRequestPromise(requestId) {
|
|
702
|
+
const promise = createDeferredPromise();
|
|
703
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_requests, "f").set(requestId, promise);
|
|
704
|
+
return promise;
|
|
705
|
+
}, _SnapKeyringV1_handleAsyncRedirect =
|
|
706
|
+
/**
|
|
707
|
+
* Handle an async redirect response from the Snap.
|
|
708
|
+
*
|
|
709
|
+
* @param redirect - The redirect information.
|
|
710
|
+
* @param redirect.message - The message to show the user.
|
|
711
|
+
* @param redirect.url - The URL to redirect to.
|
|
712
|
+
*/
|
|
713
|
+
async function _SnapKeyringV1_handleAsyncRedirect(redirect) {
|
|
714
|
+
const { message = '', url: redirectUrl = '' } = redirect;
|
|
715
|
+
const url = redirectUrl ? sanitizeUrl(redirectUrl) : redirectUrl;
|
|
716
|
+
if (url) {
|
|
717
|
+
__classPrivateFieldGet(this, _SnapKeyringV1_instances, "m", _SnapKeyringV1_validateRedirectUrl).call(this, url);
|
|
718
|
+
}
|
|
719
|
+
await __classPrivateFieldGet(this, _SnapKeyringV1_callbacks, "f").redirectUser(url, message);
|
|
720
|
+
}, _SnapKeyringV1_validateRedirectUrl = function _SnapKeyringV1_validateRedirectUrl(url) {
|
|
721
|
+
const { origin } = new URL(url);
|
|
722
|
+
const snap = this.messenger.call('SnapController:getSnap', this.snapId);
|
|
723
|
+
if (!snap) {
|
|
724
|
+
throw new Error(`Snap '${this.snapId}' not found.`);
|
|
725
|
+
}
|
|
726
|
+
const allowedOrigins = snap.manifest.initialPermissions['endowment:keyring']?.allowedOrigins ??
|
|
727
|
+
[];
|
|
728
|
+
if (!allowedOrigins.includes(origin)) {
|
|
729
|
+
throw new Error(`Redirect URL domain '${origin}' is not an allowed origin by snap '${this.snapId}'`);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
//# sourceMappingURL=SnapKeyringV1.mjs.map
|