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