@metamask/snaps-execution-environments 10.4.1 → 11.0.0
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 +8 -1
- package/dist/common/BaseSnapExecutor.cjs +129 -189
- package/dist/common/BaseSnapExecutor.cjs.map +1 -1
- package/dist/common/BaseSnapExecutor.d.cts +0 -57
- package/dist/common/BaseSnapExecutor.d.cts.map +1 -1
- package/dist/common/BaseSnapExecutor.d.mts +0 -57
- package/dist/common/BaseSnapExecutor.d.mts.map +1 -1
- package/dist/common/BaseSnapExecutor.mjs +133 -193
- package/dist/common/BaseSnapExecutor.mjs.map +1 -1
- package/dist/common/commands.cjs +23 -29
- package/dist/common/commands.cjs.map +1 -1
- package/dist/common/commands.d.cts +12 -20
- package/dist/common/commands.d.cts.map +1 -1
- package/dist/common/commands.d.mts +12 -20
- package/dist/common/commands.d.mts.map +1 -1
- package/dist/common/commands.mjs +21 -27
- package/dist/common/commands.mjs.map +1 -1
- package/dist/common/validation.cjs +13 -25
- package/dist/common/validation.cjs.map +1 -1
- package/dist/common/validation.d.cts +69 -42
- package/dist/common/validation.d.cts.map +1 -1
- package/dist/common/validation.d.mts +69 -42
- package/dist/common/validation.d.mts.map +1 -1
- package/dist/common/validation.mjs +15 -27
- package/dist/common/validation.mjs.map +1 -1
- package/dist/webpack/iframe/bundle.js +1 -1
- package/dist/webpack/node-process/bundle.js +1 -1
- package/dist/webpack/node-thread/bundle.js +1 -1
- package/dist/webpack/webview/index.html +1 -1
- package/package.json +2 -2
- package/dist/common/lockdown/lockdown.cjs +0 -29
- package/dist/common/lockdown/lockdown.cjs.map +0 -1
- package/dist/common/lockdown/lockdown.d.cts +0 -7
- package/dist/common/lockdown/lockdown.d.cts.map +0 -1
- package/dist/common/lockdown/lockdown.d.mts +0 -7
- package/dist/common/lockdown/lockdown.d.mts.map +0 -1
- package/dist/common/lockdown/lockdown.mjs +0 -25
- package/dist/common/lockdown/lockdown.mjs.map +0 -1
- package/dist/common/sortParams.cjs +0 -35
- package/dist/common/sortParams.cjs.map +0 -1
- package/dist/common/sortParams.d.cts +0 -17
- package/dist/common/sortParams.d.cts.map +0 -1
- package/dist/common/sortParams.d.mts +0 -17
- package/dist/common/sortParams.d.mts.map +0 -1
- package/dist/common/sortParams.mjs +0 -31
- package/dist/common/sortParams.mjs.map +0 -1
|
@@ -12,16 +12,15 @@ const ObjectMultiplex = $importDefault($ObjectMultiplex);
|
|
|
12
12
|
import { errorCodes, rpcErrors, serializeError } from "@metamask/rpc-errors";
|
|
13
13
|
import { getErrorData } from "@metamask/snaps-sdk";
|
|
14
14
|
import { SNAP_EXPORT_NAMES, logError, SNAP_EXPORTS, WrappedSnapError, unwrapError, logInfo } from "@metamask/snaps-utils";
|
|
15
|
-
import {
|
|
16
|
-
import { assert, isJsonRpcRequest, hasProperty, getSafeJson, JsonRpcIdStruct } from "@metamask/utils";
|
|
15
|
+
import { is } from "@metamask/superstruct";
|
|
16
|
+
import { assert, isJsonRpcRequest, hasProperty, getSafeJson, JsonRpcIdStruct, createDeferredPromise } from "@metamask/utils";
|
|
17
17
|
import { pipeline } from "readable-stream";
|
|
18
|
-
import {
|
|
18
|
+
import { assertCommandParams, getHandlerArguments } from "./commands.mjs";
|
|
19
19
|
import { createEndowments } from "./endowments/index.mjs";
|
|
20
20
|
import { addEventListener, removeEventListener } from "./globalEvents.mjs";
|
|
21
21
|
import { SnapProvider } from "./SnapProvider.mjs";
|
|
22
|
-
import { sortParamKeys } from "./sortParams.mjs";
|
|
23
22
|
import { assertEthereumOutboundRequest, assertSnapOutboundRequest, sanitizeRequestArguments, withTeardown, isValidResponse, isMultichainRequest, assertMultichainOutboundRequest } from "./utils.mjs";
|
|
24
|
-
import { ExecuteSnapRequestArgumentsStruct,
|
|
23
|
+
import { ExecuteSnapRequestArgumentsStruct, SnapRpcRequestArgumentsStruct } from "./validation.mjs";
|
|
25
24
|
import { log } from "../logging.mjs";
|
|
26
25
|
const fallbackError = {
|
|
27
26
|
code: errorCodes.rpc.internal,
|
|
@@ -32,109 +31,31 @@ const unhandledError = rpcErrors
|
|
|
32
31
|
message: 'Unhandled Snap Error',
|
|
33
32
|
})
|
|
34
33
|
.serialize();
|
|
35
|
-
/**
|
|
36
|
-
* The supported methods in the execution environment. The validator checks the
|
|
37
|
-
* incoming JSON-RPC request, and the `params` property is used for sorting the
|
|
38
|
-
* parameters, if they are an object.
|
|
39
|
-
*/
|
|
40
|
-
const EXECUTION_ENVIRONMENT_METHODS = {
|
|
41
|
-
ping: {
|
|
42
|
-
struct: PingRequestArgumentsStruct,
|
|
43
|
-
params: [],
|
|
44
|
-
},
|
|
45
|
-
executeSnap: {
|
|
46
|
-
struct: ExecuteSnapRequestArgumentsStruct,
|
|
47
|
-
params: ['snapId', 'sourceCode', 'endowments'],
|
|
48
|
-
},
|
|
49
|
-
terminate: {
|
|
50
|
-
struct: TerminateRequestArgumentsStruct,
|
|
51
|
-
params: [],
|
|
52
|
-
},
|
|
53
|
-
snapRpc: {
|
|
54
|
-
struct: SnapRpcRequestArgumentsStruct,
|
|
55
|
-
params: ['target', 'handler', 'origin', 'request'],
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
34
|
export class BaseSnapExecutor {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
66
|
-
commandStream;
|
|
67
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
68
|
-
// ignore.
|
|
69
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
70
|
-
rpcStream;
|
|
71
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
72
|
-
// ignore.
|
|
73
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
74
|
-
methods;
|
|
75
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
76
|
-
// ignore.
|
|
77
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
78
|
-
snapErrorHandler;
|
|
79
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
80
|
-
// ignore.
|
|
81
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
82
|
-
snapPromiseErrorHandler;
|
|
83
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
84
|
-
// ignore.
|
|
85
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
86
|
-
lastTeardown = 0;
|
|
35
|
+
#snapData;
|
|
36
|
+
#commandStream;
|
|
37
|
+
#rpcStream;
|
|
38
|
+
#snapErrorHandler;
|
|
39
|
+
#snapPromiseErrorHandler;
|
|
40
|
+
#teardownRef = { lastTeardown: 0 };
|
|
87
41
|
constructor(commandStream, rpcStream) {
|
|
88
|
-
this
|
|
89
|
-
this
|
|
90
|
-
this
|
|
91
|
-
|
|
92
|
-
|
|
42
|
+
this.#snapData = new Map();
|
|
43
|
+
this.#commandStream = commandStream;
|
|
44
|
+
this.#commandStream.on('data', (data) => {
|
|
45
|
+
/* istanbul ignore next 2 */
|
|
46
|
+
this.#onCommandRequest(data).catch((error) => {
|
|
93
47
|
logError(error);
|
|
94
48
|
});
|
|
95
49
|
});
|
|
96
|
-
this
|
|
97
|
-
this.methods = getCommandMethodImplementations(this.startSnap.bind(this), async (target, handlerType, args) => {
|
|
98
|
-
const data = this.snapData.get(target);
|
|
99
|
-
// We're capturing the handler in case someone modifies the data object
|
|
100
|
-
// before the call.
|
|
101
|
-
const handler = data?.exports[handlerType];
|
|
102
|
-
const { required } = SNAP_EXPORTS[handlerType];
|
|
103
|
-
assert(!required || handler !== undefined, `No ${handlerType} handler exported for snap "${target}".`, rpcErrors.methodNotSupported);
|
|
104
|
-
// Certain handlers are not required. If they are not exported, we
|
|
105
|
-
// return null.
|
|
106
|
-
if (!handler) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
const result = await this.executeInSnapContext(target, async () =>
|
|
110
|
-
// TODO: fix handler args type cast
|
|
111
|
-
handler(args));
|
|
112
|
-
// The handler might not return anything, but undefined is not valid JSON.
|
|
113
|
-
if (result === undefined || result === null) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
// /!\ Always return only sanitized JSON to prevent security flaws. /!\
|
|
117
|
-
try {
|
|
118
|
-
return getSafeJson(result);
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
throw rpcErrors.internal(`Received non-JSON-serializable value: ${error.message.replace(/^Assertion failed: /u, '')}`);
|
|
122
|
-
}
|
|
123
|
-
}, this.onTerminate.bind(this));
|
|
50
|
+
this.#rpcStream = rpcStream;
|
|
124
51
|
}
|
|
125
|
-
|
|
126
|
-
// ignore.
|
|
127
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
128
|
-
errorHandler(error, data) {
|
|
52
|
+
#errorHandler(error, data) {
|
|
129
53
|
const serializedError = serializeError(error, {
|
|
130
54
|
fallbackError: unhandledError,
|
|
131
55
|
shouldIncludeStack: false,
|
|
132
56
|
shouldPreserveMessage: false,
|
|
133
57
|
});
|
|
134
58
|
const errorData = getErrorData(serializedError);
|
|
135
|
-
// TODO: Either fix this lint violation or explain why it's necessary to
|
|
136
|
-
// ignore.
|
|
137
|
-
// eslint-disable-next-line promise/no-promise-in-callback
|
|
138
59
|
this.#notify({
|
|
139
60
|
method: 'UnhandledError',
|
|
140
61
|
params: {
|
|
@@ -150,19 +71,14 @@ export class BaseSnapExecutor {
|
|
|
150
71
|
logError(notifyError);
|
|
151
72
|
});
|
|
152
73
|
}
|
|
153
|
-
|
|
154
|
-
// ignore.
|
|
155
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
156
|
-
async onCommandRequest(message) {
|
|
74
|
+
async #onCommandRequest(message) {
|
|
157
75
|
if (!isJsonRpcRequest(message)) {
|
|
158
76
|
if (hasProperty(message, 'id') &&
|
|
159
77
|
is(message.id, JsonRpcIdStruct)) {
|
|
160
78
|
// Instead of throwing, we directly respond with an error.
|
|
161
79
|
// We can only do this if the message ID is still valid.
|
|
162
|
-
await this.#
|
|
80
|
+
await this.#respond(message.id, {
|
|
163
81
|
error: serializeError(rpcErrors.internal('JSON-RPC requests must be JSON serializable objects.')),
|
|
164
|
-
id: message.id,
|
|
165
|
-
jsonrpc: '2.0',
|
|
166
82
|
});
|
|
167
83
|
}
|
|
168
84
|
else {
|
|
@@ -171,38 +87,8 @@ export class BaseSnapExecutor {
|
|
|
171
87
|
return;
|
|
172
88
|
}
|
|
173
89
|
const { id, method, params } = message;
|
|
174
|
-
if (!hasProperty(EXECUTION_ENVIRONMENT_METHODS, method)) {
|
|
175
|
-
await this.#respond(id, {
|
|
176
|
-
error: rpcErrors
|
|
177
|
-
.methodNotFound({
|
|
178
|
-
data: {
|
|
179
|
-
method,
|
|
180
|
-
},
|
|
181
|
-
})
|
|
182
|
-
.serialize(),
|
|
183
|
-
});
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const methodObject = EXECUTION_ENVIRONMENT_METHODS[method];
|
|
187
|
-
// support params by-name and by-position
|
|
188
|
-
const paramsAsArray = sortParamKeys(methodObject.params, params);
|
|
189
|
-
const [error] = validate(paramsAsArray, methodObject.struct);
|
|
190
|
-
if (error) {
|
|
191
|
-
await this.#respond(id, {
|
|
192
|
-
error: rpcErrors
|
|
193
|
-
.invalidParams({
|
|
194
|
-
message: `Invalid parameters for method "${method}": ${error.message}.`,
|
|
195
|
-
data: {
|
|
196
|
-
method,
|
|
197
|
-
params: paramsAsArray,
|
|
198
|
-
},
|
|
199
|
-
})
|
|
200
|
-
.serialize(),
|
|
201
|
-
});
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
90
|
try {
|
|
205
|
-
const result = await this
|
|
91
|
+
const result = await this.#handleCommand(method, params);
|
|
206
92
|
await this.#respond(id, { result });
|
|
207
93
|
}
|
|
208
94
|
catch (rpcError) {
|
|
@@ -214,12 +100,83 @@ export class BaseSnapExecutor {
|
|
|
214
100
|
});
|
|
215
101
|
}
|
|
216
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Handle an incoming JSON-RPC command request.
|
|
105
|
+
*
|
|
106
|
+
* @param method - The JSON-RPC method name.
|
|
107
|
+
* @param params - The optional JSON-RPC parameters.
|
|
108
|
+
* @returns The response based on the JSON-RPC method invoked.
|
|
109
|
+
* @throws If the passed method is not available.
|
|
110
|
+
*/
|
|
111
|
+
async #handleCommand(method, params) {
|
|
112
|
+
switch (method) {
|
|
113
|
+
case 'ping':
|
|
114
|
+
return 'OK';
|
|
115
|
+
case 'terminate': {
|
|
116
|
+
this.#handleTerminate();
|
|
117
|
+
return 'OK';
|
|
118
|
+
}
|
|
119
|
+
case 'executeSnap': {
|
|
120
|
+
assertCommandParams(method, params, ExecuteSnapRequestArgumentsStruct);
|
|
121
|
+
await this.#startSnap(params);
|
|
122
|
+
return 'OK';
|
|
123
|
+
}
|
|
124
|
+
case 'snapRpc': {
|
|
125
|
+
assertCommandParams(method, params, SnapRpcRequestArgumentsStruct);
|
|
126
|
+
return await this.#invokeSnap(params);
|
|
127
|
+
}
|
|
128
|
+
default:
|
|
129
|
+
throw rpcErrors.methodNotFound({
|
|
130
|
+
data: {
|
|
131
|
+
method,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Invoke an exported handler on a running Snap.
|
|
138
|
+
*
|
|
139
|
+
* @param options - An options bag.
|
|
140
|
+
* @param options.snapId - The Snap ID.
|
|
141
|
+
* @param options.handler - The handler to invoke.
|
|
142
|
+
* @param options.origin - The origin invoking the handler.
|
|
143
|
+
* @param options.request - The JSON-RPC request to invoke the handler with.
|
|
144
|
+
* @returns The result of invoking the handler on the Snap.
|
|
145
|
+
*/
|
|
146
|
+
async #invokeSnap({ snapId, handler: handlerType, origin, request, }) {
|
|
147
|
+
const args = getHandlerArguments(origin, handlerType, request);
|
|
148
|
+
const data = this.#snapData.get(snapId);
|
|
149
|
+
// We're capturing the handler in case someone modifies the data object
|
|
150
|
+
// before the call.
|
|
151
|
+
const handler = data?.exports[handlerType];
|
|
152
|
+
const { required } = SNAP_EXPORTS[handlerType];
|
|
153
|
+
assert(!required || handler !== undefined, `No ${handlerType} handler exported for Snap "${snapId}".`, rpcErrors.methodNotSupported);
|
|
154
|
+
// Certain handlers are not required. If they are not exported, we
|
|
155
|
+
// return null.
|
|
156
|
+
if (!handler) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
const result = await this.#executeInSnapContext(snapId, async () =>
|
|
160
|
+
// TODO: fix handler args type cast
|
|
161
|
+
handler(args));
|
|
162
|
+
// The handler might not return anything, but undefined is not valid JSON.
|
|
163
|
+
if (result === undefined || result === null) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
// /!\ Always return only sanitized JSON to prevent security flaws. /!\
|
|
167
|
+
try {
|
|
168
|
+
return getSafeJson(result);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
throw rpcErrors.internal(`Received non-JSON-serializable value: ${error.message.replace(/^Assertion failed: /u, '')}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
217
174
|
// Awaitable function that writes back to the command stream
|
|
218
175
|
// To prevent snap execution from blocking writing we wrap in a promise
|
|
219
176
|
// and await it before continuing execution
|
|
220
177
|
async #write(chunk) {
|
|
221
178
|
return new Promise((resolve, reject) => {
|
|
222
|
-
this
|
|
179
|
+
this.#commandStream.write(chunk, (error) => {
|
|
223
180
|
if (error) {
|
|
224
181
|
reject(error);
|
|
225
182
|
return;
|
|
@@ -258,28 +215,29 @@ export class BaseSnapExecutor {
|
|
|
258
215
|
* Attempts to evaluate a snap in SES. Generates APIs for the snap. May throw
|
|
259
216
|
* on errors.
|
|
260
217
|
*
|
|
261
|
-
* @param
|
|
262
|
-
* @param
|
|
263
|
-
* @param
|
|
218
|
+
* @param params - The parameters.
|
|
219
|
+
* @param params.snapId - The id of the snap.
|
|
220
|
+
* @param params.sourceCode - The source code of the snap, in IIFE format.
|
|
221
|
+
* @param params.endowments - An array of the names of the endowments.
|
|
264
222
|
*/
|
|
265
|
-
async startSnap(snapId, sourceCode,
|
|
223
|
+
async #startSnap({ snapId, sourceCode, endowments: endowmentKeys, }) {
|
|
266
224
|
log(`Starting snap '${snapId}' in worker.`);
|
|
267
|
-
if (this
|
|
268
|
-
removeEventListener('unhandledrejection', this
|
|
225
|
+
if (this.#snapPromiseErrorHandler) {
|
|
226
|
+
removeEventListener('unhandledrejection', this.#snapPromiseErrorHandler);
|
|
269
227
|
}
|
|
270
|
-
if (this
|
|
271
|
-
removeEventListener('error', this
|
|
228
|
+
if (this.#snapErrorHandler) {
|
|
229
|
+
removeEventListener('error', this.#snapErrorHandler);
|
|
272
230
|
}
|
|
273
|
-
this
|
|
274
|
-
this
|
|
231
|
+
this.#snapErrorHandler = (error) => {
|
|
232
|
+
this.#errorHandler(error.error, { snapId });
|
|
275
233
|
};
|
|
276
|
-
this
|
|
277
|
-
this
|
|
234
|
+
this.#snapPromiseErrorHandler = (error) => {
|
|
235
|
+
this.#errorHandler(error instanceof Error ? error : error.reason, {
|
|
278
236
|
snapId,
|
|
279
237
|
});
|
|
280
238
|
};
|
|
281
239
|
const multiplex = new ObjectMultiplex();
|
|
282
|
-
pipeline(this
|
|
240
|
+
pipeline(this.#rpcStream, multiplex, this.#rpcStream, (error) => {
|
|
283
241
|
if (error && !error.message?.match('Premature close')) {
|
|
284
242
|
logError(`Provider stream failure.`, error);
|
|
285
243
|
}
|
|
@@ -292,8 +250,8 @@ export class BaseSnapExecutor {
|
|
|
292
250
|
rpcMiddleware: [createIdRemapMiddleware()],
|
|
293
251
|
});
|
|
294
252
|
multichainProvider.initializeSync();
|
|
295
|
-
const snap = this
|
|
296
|
-
const ethereum = this
|
|
253
|
+
const snap = this.#createSnapGlobal(provider, multichainProvider);
|
|
254
|
+
const ethereum = this.#createEIP1193Provider(provider);
|
|
297
255
|
// We specifically use any type because the Snap can modify the object any way they want
|
|
298
256
|
const snapModule = { exports: {} };
|
|
299
257
|
try {
|
|
@@ -301,18 +259,18 @@ export class BaseSnapExecutor {
|
|
|
301
259
|
snap,
|
|
302
260
|
ethereum,
|
|
303
261
|
snapId,
|
|
304
|
-
endowments:
|
|
262
|
+
endowments: endowmentKeys,
|
|
305
263
|
notify: this.#notify.bind(this),
|
|
306
264
|
});
|
|
307
265
|
// !!! Ensure that this is the only place the data is being set.
|
|
308
266
|
// Other methods access the object value and mutate its properties.
|
|
309
|
-
this
|
|
267
|
+
this.#snapData.set(snapId, {
|
|
310
268
|
idleTeardown: endowmentTeardown,
|
|
311
269
|
runningEvaluations: new Set(),
|
|
312
270
|
exports: {},
|
|
313
271
|
});
|
|
314
|
-
addEventListener('unhandledRejection', this
|
|
315
|
-
addEventListener('error', this
|
|
272
|
+
addEventListener('unhandledRejection', this.#snapPromiseErrorHandler);
|
|
273
|
+
addEventListener('error', this.#snapErrorHandler);
|
|
316
274
|
const compartment = new Compartment({
|
|
317
275
|
...endowments,
|
|
318
276
|
module: snapModule,
|
|
@@ -326,13 +284,13 @@ export class BaseSnapExecutor {
|
|
|
326
284
|
compartment.globalThis.self = compartment.globalThis;
|
|
327
285
|
compartment.globalThis.global = compartment.globalThis;
|
|
328
286
|
compartment.globalThis.window = compartment.globalThis;
|
|
329
|
-
await this
|
|
287
|
+
await this.#executeInSnapContext(snapId, async () => {
|
|
330
288
|
compartment.evaluate(sourceCode);
|
|
331
|
-
await this
|
|
289
|
+
await this.#registerSnapExports(snapId, snapModule);
|
|
332
290
|
});
|
|
333
291
|
}
|
|
334
292
|
catch (error) {
|
|
335
|
-
this
|
|
293
|
+
this.#removeSnap(snapId);
|
|
336
294
|
const [cause] = unwrapError(error);
|
|
337
295
|
throw rpcErrors.internal({
|
|
338
296
|
message: `Error while running snap '${snapId}': ${cause.message}`,
|
|
@@ -346,18 +304,15 @@ export class BaseSnapExecutor {
|
|
|
346
304
|
* Cancels all running evaluations of all snaps and clears all snap data.
|
|
347
305
|
* NOTE:** Should only be called in response to the `terminate` RPC command.
|
|
348
306
|
*/
|
|
349
|
-
|
|
307
|
+
#handleTerminate() {
|
|
350
308
|
// `stop()` tears down snap endowments.
|
|
351
309
|
// Teardown will also be run for each snap as soon as there are
|
|
352
310
|
// no more running evaluations for that snap.
|
|
353
|
-
this
|
|
354
|
-
this
|
|
311
|
+
this.#snapData.forEach((data) => data.runningEvaluations.forEach((evaluation) => evaluation.stop()));
|
|
312
|
+
this.#snapData.clear();
|
|
355
313
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
359
|
-
async registerSnapExports(snapId, snapModule) {
|
|
360
|
-
const data = this.snapData.get(snapId);
|
|
314
|
+
async #registerSnapExports(snapId, snapModule) {
|
|
315
|
+
const data = this.#snapData.get(snapId);
|
|
361
316
|
// Somebody deleted the snap before we could register.
|
|
362
317
|
if (!data) {
|
|
363
318
|
return;
|
|
@@ -382,10 +337,7 @@ export class BaseSnapExecutor {
|
|
|
382
337
|
* @param multichainProvider - A StreamProvider connected to the CAIP-27 client stream.
|
|
383
338
|
* @returns The snap provider object.
|
|
384
339
|
*/
|
|
385
|
-
|
|
386
|
-
// ignore.
|
|
387
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
388
|
-
createSnapGlobal(provider, multichainProvider) {
|
|
340
|
+
#createSnapGlobal(provider, multichainProvider) {
|
|
389
341
|
const originalRequest = provider.request.bind(provider);
|
|
390
342
|
const originalMultichainRequest = multichainProvider.request.bind(multichainProvider);
|
|
391
343
|
const request = async (args) => {
|
|
@@ -394,9 +346,9 @@ export class BaseSnapExecutor {
|
|
|
394
346
|
assertSnapOutboundRequest(sanitizedArgs);
|
|
395
347
|
if (isMultichainRequest(sanitizedArgs)) {
|
|
396
348
|
assertMultichainOutboundRequest(sanitizedArgs);
|
|
397
|
-
return await withTeardown(originalMultichainRequest(sanitizedArgs), this);
|
|
349
|
+
return await withTeardown(originalMultichainRequest(sanitizedArgs), this.#teardownRef);
|
|
398
350
|
}
|
|
399
|
-
return await withTeardown(originalRequest(sanitizedArgs), this);
|
|
351
|
+
return await withTeardown(originalRequest(sanitizedArgs), this.#teardownRef);
|
|
400
352
|
};
|
|
401
353
|
const snapsProvider = { request };
|
|
402
354
|
return harden(snapsProvider);
|
|
@@ -407,16 +359,13 @@ export class BaseSnapExecutor {
|
|
|
407
359
|
* @param provider - A StreamProvider connected to MetaMask.
|
|
408
360
|
* @returns The EIP-1193 Ethereum provider object.
|
|
409
361
|
*/
|
|
410
|
-
|
|
411
|
-
// ignore.
|
|
412
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
413
|
-
createEIP1193Provider(provider) {
|
|
362
|
+
#createEIP1193Provider(provider) {
|
|
414
363
|
const originalRequest = provider.request.bind(provider);
|
|
415
364
|
const request = async (args) => {
|
|
416
365
|
// As part of the sanitization, we validate that the args are valid JSON.
|
|
417
366
|
const sanitizedArgs = sanitizeRequestArguments(args);
|
|
418
367
|
assertEthereumOutboundRequest(sanitizedArgs);
|
|
419
|
-
return await withTeardown(originalRequest(sanitizedArgs), this);
|
|
368
|
+
return await withTeardown(originalRequest(sanitizedArgs), this.#teardownRef);
|
|
420
369
|
};
|
|
421
370
|
const ethereumProvider = { request };
|
|
422
371
|
return harden(ethereumProvider);
|
|
@@ -426,11 +375,8 @@ export class BaseSnapExecutor {
|
|
|
426
375
|
*
|
|
427
376
|
* @param snapId - The id of the snap to remove.
|
|
428
377
|
*/
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
432
|
-
removeSnap(snapId) {
|
|
433
|
-
this.snapData.delete(snapId);
|
|
378
|
+
#removeSnap(snapId) {
|
|
379
|
+
this.#snapData.delete(snapId);
|
|
434
380
|
}
|
|
435
381
|
/**
|
|
436
382
|
* Calls the specified executor function in the context of the specified snap.
|
|
@@ -443,20 +389,14 @@ export class BaseSnapExecutor {
|
|
|
443
389
|
* @returns The executor's return value.
|
|
444
390
|
* @template Result - The return value of the executor.
|
|
445
391
|
*/
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
449
|
-
async executeInSnapContext(snapId, executor) {
|
|
450
|
-
const data = this.snapData.get(snapId);
|
|
392
|
+
async #executeInSnapContext(snapId, executor) {
|
|
393
|
+
const data = this.#snapData.get(snapId);
|
|
451
394
|
if (data === undefined) {
|
|
452
395
|
throw rpcErrors.internal(`Tried to execute in context of unknown snap: "${snapId}".`);
|
|
453
396
|
}
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
rpcErrors.internal(`The snap "${snapId}" has been terminated during execution.`))));
|
|
458
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
459
|
-
const evaluationData = { stop: stop };
|
|
397
|
+
const { promise: stopPromise, reject } = createDeferredPromise();
|
|
398
|
+
const stop = () => reject(rpcErrors.internal(`The Snap "${snapId}" has been terminated during execution.`));
|
|
399
|
+
const evaluationData = { stop };
|
|
460
400
|
try {
|
|
461
401
|
data.runningEvaluations.add(evaluationData);
|
|
462
402
|
// Notice that we have to await this executor.
|
|
@@ -470,7 +410,7 @@ export class BaseSnapExecutor {
|
|
|
470
410
|
finally {
|
|
471
411
|
data.runningEvaluations.delete(evaluationData);
|
|
472
412
|
if (data.runningEvaluations.size === 0) {
|
|
473
|
-
this.lastTeardown += 1;
|
|
413
|
+
this.#teardownRef.lastTeardown += 1;
|
|
474
414
|
await data.idleTeardown();
|
|
475
415
|
}
|
|
476
416
|
}
|