From 36fd33b8caf31c16c75babcb619a7d2ecba2af67 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Wed, 11 Oct 2023 13:03:43 +0200 Subject: [PATCH 1/4] Bump MetaMask core packages (#1818) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rebased and updated version of https://github.com/MetaMask/snaps/pull/1728 - Updates and uses `@metamask/rpc-errors` everywhere - Updates and uses `@metamask/json-rpc-engine` everywhere - Updates the following packages to latest: `@metamask/permission-controller`, `@metamask/approval-controller`, `@metamask/providers`, `@metamask/eth-json-rpc-middleware` Huge thank you to @legobeat for all of the help landing this ❤️ Blocked by https://github.com/MetaMask/snaps/issues/1823 --------- Co-authored-by: legobt <6wbvkn0j@anonaddy.me> Co-authored-by: Maarten Zuidhoorn --- ...odules-polyfill-npm-0.2.2-f612681798.patch | 26 ++ package.json | 1 + packages/examples/packages/bip32/package.json | 2 +- .../packages/bip32/snap.manifest.json | 2 +- packages/examples/packages/bip44/package.json | 2 +- .../packages/bip44/snap.manifest.json | 2 +- .../packages/browserify-plugin/package.json | 2 +- .../browserify-plugin/snap.manifest.json | 2 +- .../examples/packages/browserify/package.json | 2 +- .../packages/browserify/snap.manifest.json | 2 +- .../examples/packages/cronjobs/package.json | 2 +- .../packages/cronjobs/snap.manifest.json | 2 +- .../examples/packages/dialogs/package.json | 2 +- .../packages/dialogs/snap.manifest.json | 2 +- .../packages/ethereum-provider/package.json | 2 +- .../ethereum-provider/snap.manifest.json | 2 +- .../examples/packages/ethers-js/package.json | 2 +- .../packages/ethers-js/snap.manifest.json | 2 +- .../packages/get-entropy/package.json | 2 +- .../packages/get-entropy/snap.manifest.json | 2 +- .../packages/get-file/snap.manifest.json | 2 +- .../examples/packages/get-locale/package.json | 2 +- .../packages/get-locale/snap.manifest.json | 2 +- .../packages/consumer-signer/package.json | 2 +- .../consumer-signer/snap.manifest.json | 2 +- .../packages/core-signer/package.json | 2 +- .../packages/core-signer/snap.manifest.json | 2 +- .../examples/packages/json-rpc/package.json | 2 +- .../packages/json-rpc/snap.manifest.json | 2 +- .../packages/manage-state/package.json | 2 +- .../packages/manage-state/snap.manifest.json | 2 +- .../packages/network-access/package.json | 2 +- .../network-access/snap.manifest.json | 2 +- .../packages/notifications/package.json | 2 +- .../packages/notifications/snap.manifest.json | 2 +- .../packages/rollup-plugin/package.json | 2 +- .../packages/rollup-plugin/snap.manifest.json | 2 +- packages/examples/packages/wasm/package.json | 2 +- .../examples/packages/wasm/snap.manifest.json | 2 +- .../packages/webpack-plugin/package.json | 2 +- .../webpack-plugin/snap.manifest.json | 2 +- packages/snaps-controllers/package.json | 23 +- .../src/services/AbstractExecutionService.ts | 76 ++-- .../src/services/browser.test.ts | 1 + .../node/NodeProcessExecutionService.test.ts | 26 +- .../node/NodeThreadExecutionService.test.ts | 26 +- .../src/snaps/SnapController.test.ts | 15 +- .../src/snaps/SnapController.ts | 6 +- .../src/snaps/endowments/cronjob.ts | 8 +- .../src/snaps/endowments/keyring.ts | 8 +- .../src/snaps/endowments/name-lookup.ts | 6 +- .../src/snaps/endowments/rpc.ts | 8 +- .../snaps/endowments/transaction-insight.ts | 6 +- .../src/test-utils/controller.ts | 4 +- .../src/test-utils/execution-environment.ts | 2 +- .../src/test-utils/service.ts | 2 +- .../coverage.json | 6 +- .../lavamoat/browserify/iframe/policy.json | 46 +-- .../browserify/node-process/policy.json | 55 +-- .../browserify/node-thread/policy.json | 55 +-- .../browserify/worker-executor/policy.json | 46 +-- .../lavamoat/build-system/policy.json | 2 +- .../snaps-execution-environments/package.json | 6 +- .../common/BaseSnapExecutor.test.browser.ts | 21 +- .../src/common/BaseSnapExecutor.ts | 22 +- .../src/common/endowments/index.ts | 4 +- .../src/common/endowments/interval.ts | 4 +- .../src/common/endowments/timeout.ts | 4 +- .../src/common/globalEvents.ts | 4 +- .../src/common/test-utils/endowments.ts | 2 +- .../src/common/utils.ts | 14 +- .../src/common/validation.ts | 6 +- packages/snaps-rpc-methods/jest.config.js | 6 +- packages/snaps-rpc-methods/package.json | 7 +- .../src/permitted/common/snapInstallation.ts | 6 +- .../src/permitted/getFile.test.ts | 24 +- .../src/permitted/getFile.ts | 13 +- .../src/permitted/invokeKeyring.test.ts | 23 +- .../src/permitted/invokeKeyring.ts | 10 +- .../src/permitted/invokeSnapSugar.test.ts | 4 +- .../src/permitted/invokeSnapSugar.ts | 8 +- .../src/permitted/middleware.test.ts | 7 +- .../src/permitted/middleware.ts | 9 +- .../src/permitted/requestSnaps.test.ts | 8 +- .../src/permitted/requestSnaps.ts | 4 +- .../restricted/caveats/permittedCoinTypes.ts | 12 +- .../caveats/permittedDerivationPaths.ts | 8 +- .../src/restricted/caveats/snapIds.ts | 4 +- .../src/restricted/dialog.ts | 10 +- .../src/restricted/getBip32Entropy.ts | 4 +- .../src/restricted/getBip32PublicKey.ts | 6 +- .../src/restricted/getBip44Entropy.ts | 4 +- .../src/restricted/getEntropy.ts | 4 +- .../src/restricted/getLocale.ts | 3 +- .../src/restricted/invokeSnap.ts | 6 +- .../src/restricted/manageState.test.ts | 4 +- .../src/restricted/manageState.ts | 16 +- .../src/restricted/notify.ts | 10 +- packages/snaps-simulator/package.json | 10 +- .../features/simulation/middleware.test.ts | 2 +- .../src/features/simulation/middleware.ts | 20 +- .../src/features/simulation/sagas.ts | 2 +- packages/snaps-types/package.json | 2 +- packages/snaps-utils/package.json | 4 +- packages/snaps-utils/src/errors.test.ts | 4 +- packages/snaps-utils/src/snaps.ts | 12 +- packages/test-snaps/package.json | 2 +- yarn.lock | 382 +++++++++--------- 108 files changed, 655 insertions(+), 624 deletions(-) create mode 100644 .yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch diff --git a/.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch b/.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch new file mode 100644 index 0000000000..c37acd84ee --- /dev/null +++ b/.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch @@ -0,0 +1,26 @@ +diff --git a/dist/polyfills.js b/dist/polyfills.js +index 4f4f95302a6da4643b822262921f668abc8c3db1..78202915fa5c952222aa1751442f7a9410d73dc4 100644 +--- a/dist/polyfills.js ++++ b/dist/polyfills.js +@@ -10,7 +10,7 @@ function builtinsPolyfills() { + libs.set('util', require.resolve('rollup-plugin-node-polyfills/polyfills/util')); + libs.set('sys', libs.get('util')); + libs.set('events', require.resolve('rollup-plugin-node-polyfills/polyfills/events')); +- libs.set('stream', require.resolve('rollup-plugin-node-polyfills/polyfills/stream')); ++ libs.set('stream', require.resolve('stream-browserify')); + libs.set('path', require.resolve('rollup-plugin-node-polyfills/polyfills/path')); + libs.set('querystring', require.resolve('rollup-plugin-node-polyfills/polyfills/qs')); + libs.set('punycode', require.resolve('rollup-plugin-node-polyfills/polyfills/punycode')); +diff --git a/esm/polyfills.js b/esm/polyfills.js +index 28b0ea77e1f7bc5766737caaa8cb5c5db25a279e..ae71645a1d24c46fc7987135aff0332e89017ec1 100644 +--- a/esm/polyfills.js ++++ b/esm/polyfills.js +@@ -7,7 +7,7 @@ export function builtinsPolyfills() { + libs.set('util', require.resolve('rollup-plugin-node-polyfills/polyfills/util')); + libs.set('sys', libs.get('util')); + libs.set('events', require.resolve('rollup-plugin-node-polyfills/polyfills/events')); +- libs.set('stream', require.resolve('rollup-plugin-node-polyfills/polyfills/stream')); ++ libs.set('stream', require.resolve('stream-browserify')); + libs.set('path', require.resolve('rollup-plugin-node-polyfills/polyfills/path')); + libs.set('querystring', require.resolve('rollup-plugin-node-polyfills/polyfills/qs')); + libs.set('punycode', require.resolve('rollup-plugin-node-polyfills/polyfills/punycode')); diff --git a/package.json b/package.json index 72e82e9fb9..1295a8ef46 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ }, "resolutions": { "@babel/core": "patch:@babel/core@npm%3A7.23.2#./.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch", + "@esbuild-plugins/node-modules-polyfill@^0.2.2": "patch:@esbuild-plugins/node-modules-polyfill@npm%3A0.2.2#./.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch", "@lavamoat/lavapack@^5.4.1": "patch:@lavamoat/lavapack@npm%3A5.4.1#./.yarn/patches/@lavamoat-lavapack-npm-5.4.1-2548b3e225.patch", "@types/glob@*": "patch:@types/glob@npm%3A7.1.4#./.yarn/patches/@types-glob-npm-7.1.4-d45247eaa2.patch", "@types/glob@^7.1.1": "patch:@types/glob@npm%3A7.1.4#./.yarn/patches/@types-glob-npm-7.1.4-d45247eaa2.patch", diff --git a/packages/examples/packages/bip32/package.json b/packages/examples/packages/bip32/package.json index 853954a251..531e26f546 100644 --- a/packages/examples/packages/bip32/package.json +++ b/packages/examples/packages/bip32/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@metamask/key-tree": "^9.0.0", - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0", diff --git a/packages/examples/packages/bip32/snap.manifest.json b/packages/examples/packages/bip32/snap.manifest.json index c565d8a049..be5698783f 100644 --- a/packages/examples/packages/bip32/snap.manifest.json +++ b/packages/examples/packages/bip32/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "k9a+qc7tJNdko1Kb1m9GsjwODLwJipQaW4OZt9KHFsY=", + "shasum": "O0Ncs/BuMYDj6nUxHCvIn1rO5BkQi5o7bFSs9Yw3lH8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/bip44/package.json b/packages/examples/packages/bip44/package.json index a7b04fd1ae..345aeab08d 100644 --- a/packages/examples/packages/bip44/package.json +++ b/packages/examples/packages/bip44/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@metamask/key-tree": "^9.0.0", - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0", diff --git a/packages/examples/packages/bip44/snap.manifest.json b/packages/examples/packages/bip44/snap.manifest.json index d9d92f9373..57277d727f 100644 --- a/packages/examples/packages/bip44/snap.manifest.json +++ b/packages/examples/packages/bip44/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "x/St7jjXmPv5SXyAWe44u8aFB8TRfKx4B7oRBrWNv4k=", + "shasum": "MgSpx86WLoY6ebo+Ta3qcCO9yiuIdp5mY8FlooDZJZg=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify-plugin/package.json b/packages/examples/packages/browserify-plugin/package.json index 2d8165372c..8ef699a363 100644 --- a/packages/examples/packages/browserify-plugin/package.json +++ b/packages/examples/packages/browserify-plugin/package.json @@ -30,7 +30,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/browserify-plugin/snap.manifest.json b/packages/examples/packages/browserify-plugin/snap.manifest.json index beea1b8815..d9faa38ec6 100644 --- a/packages/examples/packages/browserify-plugin/snap.manifest.json +++ b/packages/examples/packages/browserify-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "uAC3J0T7DOshJ+th+YB9RxUNTx/5I1XLgcJqHkTB6Ek=", + "shasum": "UmRhDLrx9n3NZ/qrj0O8kdRLXgb3rLKd4hMeSWaK61o=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/browserify/package.json b/packages/examples/packages/browserify/package.json index 240e6badf8..5a96ac61f0 100644 --- a/packages/examples/packages/browserify/package.json +++ b/packages/examples/packages/browserify/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/browserify/snap.manifest.json b/packages/examples/packages/browserify/snap.manifest.json index 1af55753c2..8898c3d478 100644 --- a/packages/examples/packages/browserify/snap.manifest.json +++ b/packages/examples/packages/browserify/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "vdovVqAuPpZUhBv7sp9jeWcbswewQ+b1/DmLtqMzCo4=", + "shasum": "ARCsvmYbTNhSuYkKrCGu7Js9UKIy1x0+tMconKGJ5/U=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/cronjobs/package.json b/packages/examples/packages/cronjobs/package.json index 6365cd2d54..b64b86a4b1 100644 --- a/packages/examples/packages/cronjobs/package.json +++ b/packages/examples/packages/cronjobs/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^" }, diff --git a/packages/examples/packages/cronjobs/snap.manifest.json b/packages/examples/packages/cronjobs/snap.manifest.json index 0a5fbf5783..5d1b045822 100644 --- a/packages/examples/packages/cronjobs/snap.manifest.json +++ b/packages/examples/packages/cronjobs/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "IdCbn1U58HdyfhTTSg2i6cYEmxe10PFyaq9b/h2vJF4=", + "shasum": "cak4KyQA/ZFVTwJjOAmWw3df6z4XJnD+KTd1yZVZQdo=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/dialogs/package.json b/packages/examples/packages/dialogs/package.json index 934c348d1d..c593f12f97 100644 --- a/packages/examples/packages/dialogs/package.json +++ b/packages/examples/packages/dialogs/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^" }, diff --git a/packages/examples/packages/dialogs/snap.manifest.json b/packages/examples/packages/dialogs/snap.manifest.json index 5d6a6cb4b4..aeee3f2c0d 100644 --- a/packages/examples/packages/dialogs/snap.manifest.json +++ b/packages/examples/packages/dialogs/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "ESPW+UbPbHYlyKxEc4UHB/GviiwDyzAkf+HdJsHieG0=", + "shasum": "49Ki82EdQq7D43dfQc6FSZ9BOs0gDniYJC6n5KxvoVo=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/ethereum-provider/package.json b/packages/examples/packages/ethereum-provider/package.json index e80630756d..74fd1c4c4f 100644 --- a/packages/examples/packages/ethereum-provider/package.json +++ b/packages/examples/packages/ethereum-provider/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/utils": "^8.1.0" }, diff --git a/packages/examples/packages/ethereum-provider/snap.manifest.json b/packages/examples/packages/ethereum-provider/snap.manifest.json index 8cefbbcd6d..4c9823e26e 100644 --- a/packages/examples/packages/ethereum-provider/snap.manifest.json +++ b/packages/examples/packages/ethereum-provider/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "Oyxv5Zjx6cNafk2unlOO0QvIaMhrvFGldQAarIYhEvc=", + "shasum": "SxE+1qPN5yMQft8XQ2fzE8zVytVNwBOUbysNM0Xzro8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/ethers-js/package.json b/packages/examples/packages/ethers-js/package.json index c2b614e432..f236ba924b 100644 --- a/packages/examples/packages/ethers-js/package.json +++ b/packages/examples/packages/ethers-js/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "ethers": "^6.3.0" diff --git a/packages/examples/packages/ethers-js/snap.manifest.json b/packages/examples/packages/ethers-js/snap.manifest.json index ddcc4ba4e0..fa607bc0c6 100644 --- a/packages/examples/packages/ethers-js/snap.manifest.json +++ b/packages/examples/packages/ethers-js/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "ZL0wwUmwZ8edoJXU/IS2nQdEEguzTHDB7+BGZOZFh+c=", + "shasum": "dgCNzP9t4aozrGzU4T42NENKgKTBa+H6GOynvc0NVd0=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-entropy/package.json b/packages/examples/packages/get-entropy/package.json index e53169c76f..4f099d5436 100644 --- a/packages/examples/packages/get-entropy/package.json +++ b/packages/examples/packages/get-entropy/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0", diff --git a/packages/examples/packages/get-entropy/snap.manifest.json b/packages/examples/packages/get-entropy/snap.manifest.json index 7f5d43fc6c..a94497790b 100644 --- a/packages/examples/packages/get-entropy/snap.manifest.json +++ b/packages/examples/packages/get-entropy/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "b8RW5eakG1s2QVux4dCWZ3onMb+YfphwqwWoGsdxr6k=", + "shasum": "/vHSKNUGmy1NiS/YgXRopAXAhh5ziQuN4/G+tu8kNfQ=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-file/snap.manifest.json b/packages/examples/packages/get-file/snap.manifest.json index be104f029b..a1f3738330 100644 --- a/packages/examples/packages/get-file/snap.manifest.json +++ b/packages/examples/packages/get-file/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "KeR2SaLL7hPmuTOW4n1Tz0VhgMEYqUTmjIMJHjdxBC8=", + "shasum": "AC9lKxpF32Lg5eUx+cl46l9hWzB940tYCWkxsCZNcIs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-locale/package.json b/packages/examples/packages/get-locale/package.json index 61eae6ebb9..1077cac22e 100644 --- a/packages/examples/packages/get-locale/package.json +++ b/packages/examples/packages/get-locale/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0" diff --git a/packages/examples/packages/get-locale/snap.manifest.json b/packages/examples/packages/get-locale/snap.manifest.json index 32219b2609..31c0c332a6 100644 --- a/packages/examples/packages/get-locale/snap.manifest.json +++ b/packages/examples/packages/get-locale/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "ijyKIyKjBqGaHpflKJ3YwRbIv1cqgB7Y0Dt5aq5KFcs=", + "shasum": "VDLdN2iWLu1ishmLKq0ylxJbSTaVgpMVawinDjgDNOo=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json b/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json index 95db64d24e..c4fdfde262 100644 --- a/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json +++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@metamask/key-tree": "^9.0.0", - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/utils": "^8.1.0", "@noble/hashes": "^1.3.1" diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json index fc10331010..de8bac36dd 100644 --- a/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json +++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "2Vv7GrTwARIvdLtIBvL3gI67HvqWqqWgebf7MsHxO58=", + "shasum": "b+JT7CvletGtdV7YQ7N3MQA+vMLKsLODVKCbMdJbNgs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/package.json b/packages/examples/packages/invoke-snap/packages/core-signer/package.json index e1313e5cba..9cc89b97d2 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/package.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@metamask/key-tree": "^9.0.0", - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0", diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json index d358f0cb45..4bce08080f 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "nfmXCbGaOoKZRnBDB6QXT8NwKwRqjd4EPX6e4oKWjC8=", + "shasum": "7eoQVjy43iBLByj9iYPD6VWPybret4NQeIQXjomeZC8=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/json-rpc/package.json b/packages/examples/packages/json-rpc/package.json index 5e40a3910e..ed7c371903 100644 --- a/packages/examples/packages/json-rpc/package.json +++ b/packages/examples/packages/json-rpc/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/json-rpc/snap.manifest.json b/packages/examples/packages/json-rpc/snap.manifest.json index 05e0adcab5..b578e095bf 100644 --- a/packages/examples/packages/json-rpc/snap.manifest.json +++ b/packages/examples/packages/json-rpc/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "GrWPU9PgPe8FBLSjP25Afh0zlgpt9KFUJbNAxmyEcUI=", + "shasum": "kOLxHm0PDeimorD5665ajVClMbCgq4dcR47P4sYg57c=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/manage-state/package.json b/packages/examples/packages/manage-state/package.json index 3fdc851653..de2bf8709e 100644 --- a/packages/examples/packages/manage-state/package.json +++ b/packages/examples/packages/manage-state/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/manage-state/snap.manifest.json b/packages/examples/packages/manage-state/snap.manifest.json index 541efc9cd8..aa9d6dde8a 100644 --- a/packages/examples/packages/manage-state/snap.manifest.json +++ b/packages/examples/packages/manage-state/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "+A4Ojlwr+u3QRGbXEW7tNtKyI/NFODGofdcaqH5a304=", + "shasum": "OGmcoozWOgIrP/NOhtsXhD17TQfrXii7M7DNFbBDHhA=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/network-access/package.json b/packages/examples/packages/network-access/package.json index 40a82031a0..f8e654484a 100644 --- a/packages/examples/packages/network-access/package.json +++ b/packages/examples/packages/network-access/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^", "@metamask/utils": "^8.1.0" }, diff --git a/packages/examples/packages/network-access/snap.manifest.json b/packages/examples/packages/network-access/snap.manifest.json index 4f42abc26a..2de35773fc 100644 --- a/packages/examples/packages/network-access/snap.manifest.json +++ b/packages/examples/packages/network-access/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "XkHPB72SdS3CYg4H0k3Xwab7tX/UOw76tazPRaeKfR4=", + "shasum": "DKPHWa48xahHeEDbVo/RM0SRpYVoVdUj93PkQVxTF/4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/notifications/package.json b/packages/examples/packages/notifications/package.json index 02d068fe44..409e7bab2a 100644 --- a/packages/examples/packages/notifications/package.json +++ b/packages/examples/packages/notifications/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/notifications/snap.manifest.json b/packages/examples/packages/notifications/snap.manifest.json index e64fca4526..3fd5d2599a 100644 --- a/packages/examples/packages/notifications/snap.manifest.json +++ b/packages/examples/packages/notifications/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "qS5I021eGSLim3V5i79oANEIqhtT3L6R825Rtb6OxJ8=", + "shasum": "TZW+CtPEHAya0jED3VY90IvX8vCMIba1kd/zGVm/KBs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/rollup-plugin/package.json b/packages/examples/packages/rollup-plugin/package.json index cc075011c6..df1df2c869 100644 --- a/packages/examples/packages/rollup-plugin/package.json +++ b/packages/examples/packages/rollup-plugin/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/rollup-plugin/snap.manifest.json b/packages/examples/packages/rollup-plugin/snap.manifest.json index a2b249479c..185d3aab5e 100644 --- a/packages/examples/packages/rollup-plugin/snap.manifest.json +++ b/packages/examples/packages/rollup-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "O2axJTUpSWByXOxPFDp5ygiCVcbARq378JkTyceUB6c=", + "shasum": "zJNV080XJ2XRMTMdBtfHJrkiL+i1f/EE8X9sK7xGJdk=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/wasm/package.json b/packages/examples/packages/wasm/package.json index 8fba55f11a..40f274930d 100644 --- a/packages/examples/packages/wasm/package.json +++ b/packages/examples/packages/wasm/package.json @@ -32,7 +32,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/wasm/snap.manifest.json b/packages/examples/packages/wasm/snap.manifest.json index a16a4a45ad..f3ca0c5180 100644 --- a/packages/examples/packages/wasm/snap.manifest.json +++ b/packages/examples/packages/wasm/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "Z1nuI6uEx0AkzaWRA8MbxP4KA93SOmlXsUZE2KZWcFA=", + "shasum": "G6jLmVw81D5Y2tzvcbwBgxkOrcYn/hFMoruaZavY6P4=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/webpack-plugin/package.json b/packages/examples/packages/webpack-plugin/package.json index 62c1faa17b..956f7399d1 100644 --- a/packages/examples/packages/webpack-plugin/package.json +++ b/packages/examples/packages/webpack-plugin/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/webpack-plugin/snap.manifest.json b/packages/examples/packages/webpack-plugin/snap.manifest.json index ccd522933e..abd541520f 100644 --- a/packages/examples/packages/webpack-plugin/snap.manifest.json +++ b/packages/examples/packages/webpack-plugin/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "CHsW41YUXAecl+03+ViwwQmN1WmK3nVsXOaOjUxJ2QM=", + "shasum": "DEL7I08zjOgMEr3Ck4IFie2Ly0T9fSQL4TUKZArIhuU=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index a281f443f2..3a810768b0 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -42,22 +42,23 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/approval-controller": "^3.5.0", + "@metamask/approval-controller": "^4.0.0", "@metamask/base-controller": "^3.2.0", + "@metamask/json-rpc-engine": "^7.1.1", "@metamask/object-multiplex": "^1.2.0", - "@metamask/permission-controller": "^4.1.2", + "@metamask/permission-controller": "^5.0.0", "@metamask/post-message-stream": "^7.0.0", + "@metamask/rpc-errors": "^6.1.0", + "@metamask/snaps-execution-environments": "workspace:^", "@metamask/snaps-registry": "^2.1.0", "@metamask/snaps-rpc-methods": "workspace:^", "@metamask/snaps-utils": "workspace:^", "@metamask/utils": "^8.1.0", "@xstate/fsm": "^2.0.0", "concat-stream": "^2.0.0", - "eth-rpc-errors": "^4.0.3", "gunzip-maybe": "^1.4.2", "immer": "^9.0.6", - "json-rpc-engine": "^6.1.0", - "json-rpc-middleware-stream": "^4.2.0", + "json-rpc-middleware-stream": "^5.0.0", "nanoid": "^3.1.31", "readable-web-to-node-stream": "^3.0.2", "tar-stream": "^3.1.6" @@ -134,5 +135,17 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" + }, + "lavamoat": { + "allowScripts": { + "@metamask/permission-controller>@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>keccak": false, + "@metamask/permission-controller>@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>secp256k1": false, + "@swc/core": false, + "@wdio/browser-runner>@originjs/vite-plugin-commonjs>esbuild": false, + "@wdio/cli>@wdio/utils>edgedriver": false, + "esbuild": false, + "wdio-chromedriver-service>chromedriver": false, + "wdio-geckodriver-service>geckodriver": false + } } } diff --git a/packages/snaps-controllers/src/services/AbstractExecutionService.ts b/packages/snaps-controllers/src/services/AbstractExecutionService.ts index 507f492d9d..be2500ef3e 100644 --- a/packages/snaps-controllers/src/services/AbstractExecutionService.ts +++ b/packages/snaps-controllers/src/services/AbstractExecutionService.ts @@ -1,15 +1,20 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import ObjectMultiplex from '@metamask/object-multiplex'; import type { BasePostMessageStream } from '@metamask/post-message-stream'; import type { SnapRpcHook, SnapRpcHookArgs } from '@metamask/snaps-utils'; import { SNAP_STREAM_NAMES, logError } from '@metamask/snaps-utils'; -import type { Json, JsonRpcNotification } from '@metamask/utils'; -import { Duration, isJsonRpcNotification, isObject } from '@metamask/utils'; import type { - // TODO: Replace with @metamask/utils version after bumping json-rpc-engine + Json, + JsonRpcNotification, JsonRpcRequest, PendingJsonRpcResponse, -} from 'json-rpc-engine'; -import { JsonRpcEngine } from 'json-rpc-engine'; +} from '@metamask/utils'; +import { + Duration, + hasProperty, + isJsonRpcNotification, + isObject, +} from '@metamask/utils'; import { createStreamMiddleware } from 'json-rpc-middleware-stream'; import { nanoid } from 'nanoid'; import { pipeline } from 'stream'; @@ -47,6 +52,10 @@ export type Job = { worker: WorkerType; }; +export class ExecutionEnvironmentError extends Error { + cause?: Json; +} + export abstract class AbstractExecutionService implements ExecutionService { @@ -219,10 +228,7 @@ export abstract class AbstractExecutionService ): Promise<{ streams: JobStreams; worker: WorkerType }> { const { worker, stream: envStream } = await this.initEnvStream(jobId); // Typecast justification: stream type mismatch - const mux = setupMultiplex( - envStream as unknown as Duplex, - `Job: "${jobId}"`, - ); + const mux = setupMultiplex(envStream, `Job: "${jobId}"`); const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND); @@ -230,7 +236,7 @@ export abstract class AbstractExecutionService // Also keep track of outbound request/responses const notificationHandler = ( message: - | JsonRpcRequest + | JsonRpcRequest | JsonRpcNotification>, ) => { if (!isJsonRpcNotification(message)) { @@ -273,7 +279,7 @@ export abstract class AbstractExecutionService // Typecast: stream type mismatch return { streams: { - command: commandStream as unknown as Duplex, + command: commandStream, rpc: rpcStream, // eslint-disable-next-line @typescript-eslint/naming-convention _connection: envStream, @@ -347,7 +353,7 @@ export abstract class AbstractExecutionService id: nanoid(), }); - const rpcStream = job.streams.rpc as unknown as Duplex; + const rpcStream = job.streams.rpc; this.setupSnapProvider(snapData.snapId, rpcStream); @@ -364,8 +370,8 @@ export abstract class AbstractExecutionService // Cannot be hash private yet because of tests. private async command( jobId: string, - message: JsonRpcRequest, - ): Promise { + message: JsonRpcRequest, + ): Promise { if (typeof message !== 'object') { throw new Error('Must send object.'); } @@ -376,11 +382,23 @@ export abstract class AbstractExecutionService } log('Parent: Sending Command', message); - const response: PendingJsonRpcResponse = - await job.rpcEngine.handle(message); + const response: PendingJsonRpcResponse = await job.rpcEngine.handle( + message, + ); + if (response.error) { - throw new Error(response.error.message); + const error = new ExecutionEnvironmentError(response.error.message); + if ( + isObject(response.error.data) && + hasProperty(response.error.data, 'cause') && + response.error.data.cause !== null + ) { + error.cause = response.error.data.cause; + } + + throw error; } + return response.result; } @@ -397,7 +415,7 @@ export abstract class AbstractExecutionService params: { origin, handler, - request, + request: request as JsonRpcRequest, target: snapId, }, }); @@ -433,7 +451,7 @@ export abstract class AbstractExecutionService snapId: string, options: SnapRpcHookArgs, ): Promise { - const rpcRequestHandler = await this.getRpcRequestHandler(snapId); + const rpcRequestHandler = this.getRpcRequestHandler(snapId); if (!rpcRequestHandler) { throw new Error( @@ -457,18 +475,12 @@ export function setupMultiplex( streamName: string, ): ObjectMultiplex { const mux = new ObjectMultiplex(); - pipeline( - connectionStream, - // Typecast: stream type mismatch - mux as unknown as Duplex, - connectionStream, - (error) => { - if (error) { - streamName - ? logError(`"${streamName}" stream failure.`, error) - : logError(error); - } - }, - ); + pipeline(connectionStream, mux, connectionStream, (error) => { + if (error) { + streamName + ? logError(`"${streamName}" stream failure.`, error) + : logError(error); + } + }); return mux; } diff --git a/packages/snaps-controllers/src/services/browser.test.ts b/packages/snaps-controllers/src/services/browser.test.ts index caaeff1548..a20f0ded01 100644 --- a/packages/snaps-controllers/src/services/browser.test.ts +++ b/packages/snaps-controllers/src/services/browser.test.ts @@ -8,6 +8,7 @@ describe('browser entrypoint', () => { 'OffscreenExecutionService', 'WebWorkerExecutionService', 'ProxyPostMessageStream', + 'ExecutionEnvironmentError', ]; it('entrypoint has expected exports', () => { diff --git a/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts b/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts index 131a1acce5..eddc8ca799 100644 --- a/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts +++ b/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts @@ -2,6 +2,7 @@ import type { SnapId } from '@metamask/snaps-utils'; import { HandlerType } from '@metamask/snaps-utils'; import { createService, MOCK_BLOCK_NUMBER } from '../../test-utils'; +import { ExecutionEnvironmentError } from '../AbstractExecutionService'; import type { SnapErrorJson } from '../ExecutionService'; import { NodeProcessExecutionService } from './NodeProcessExecutionService'; @@ -47,7 +48,7 @@ describe('NodeProcessExecutionService', () => { }); it('can handle errors in request handler', async () => { - expect.assertions(1); + expect.assertions(2); const { service } = createService(NodeProcessExecutionService); const snapId = 'TestSnap'; await service.executeSnap({ @@ -58,8 +59,8 @@ describe('NodeProcessExecutionService', () => { endowments: [], }); - await expect( - service.handleRpcRequest(snapId, { + const result = await service + .handleRpcRequest(snapId, { origin: 'fooOrigin', handler: ON_RPC_REQUEST, request: { @@ -68,8 +69,18 @@ describe('NodeProcessExecutionService', () => { params: {}, id: 1, }, - }), - ).rejects.toThrow('foobar'); + }) + .catch((error) => error); + + expect(result).toBeInstanceOf(ExecutionEnvironmentError); + + // eslint-disable-next-line jest/prefer-strict-equal + expect((result as ExecutionEnvironmentError).cause).toEqual({ + // TODO: Unwrap errors, and change this to the actual error message. + message: 'foobar', + stack: expect.any(String), + }); + await service.terminateAllSnaps(); }); @@ -126,9 +137,10 @@ describe('NodeProcessExecutionService', () => { code: -32603, data: { snapId: 'TestSnap', - stack: expect.any(String), + stack: expect.stringContaining('Error: random error inside'), }, - message: 'random error inside', + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }); await service.terminateAllSnaps(); diff --git a/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts b/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts index 15f1cce4dc..437af71f8e 100644 --- a/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts +++ b/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts @@ -2,6 +2,7 @@ import type { SnapId } from '@metamask/snaps-utils'; import { HandlerType } from '@metamask/snaps-utils'; import { createService, MOCK_BLOCK_NUMBER } from '../../test-utils'; +import { ExecutionEnvironmentError } from '../AbstractExecutionService'; import type { SnapErrorJson } from '../ExecutionService'; import { NodeThreadExecutionService } from './NodeThreadExecutionService'; @@ -47,7 +48,7 @@ describe('NodeThreadExecutionService', () => { }); it('can handle errors in request handler', async () => { - expect.assertions(1); + expect.assertions(2); const { service } = createService(NodeThreadExecutionService); const snapId = 'TestSnap'; await service.executeSnap({ @@ -58,8 +59,8 @@ describe('NodeThreadExecutionService', () => { endowments: [], }); - await expect( - service.handleRpcRequest(snapId, { + const result = await service + .handleRpcRequest(snapId, { origin: 'fooOrigin', handler: ON_RPC_REQUEST, request: { @@ -68,8 +69,18 @@ describe('NodeThreadExecutionService', () => { params: {}, id: 1, }, - }), - ).rejects.toThrow('foobar'); + }) + .catch((error) => error); + + expect(result).toBeInstanceOf(ExecutionEnvironmentError); + + // eslint-disable-next-line jest/prefer-strict-equal + expect((result as ExecutionEnvironmentError).cause).toEqual({ + // TODO: Unwrap errors, and change this to the actual error message. + message: 'foobar', + stack: expect.any(String), + }); + await service.terminateAllSnaps(); }); @@ -126,9 +137,10 @@ describe('NodeThreadExecutionService', () => { code: -32603, data: { snapId: 'TestSnap', - stack: expect.any(String), + stack: expect.stringContaining('Error: random error inside'), }, - message: 'random error inside', + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }); await service.terminateAllSnaps(); diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.ts b/packages/snaps-controllers/src/snaps/SnapController.test.ts index 76db301d74..bd3029b732 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.test.ts @@ -1,9 +1,14 @@ import { getPersistentState } from '@metamask/base-controller'; +import { + createAsyncMiddleware, + JsonRpcEngine, +} from '@metamask/json-rpc-engine'; import type { Caveat, SubjectPermissions, ValidPermission, } from '@metamask/permission-controller'; +import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods'; import type { RpcOrigins, @@ -37,9 +42,7 @@ import { } from '@metamask/snaps-utils/test-utils'; import type { SemVerRange, SemVerVersion } from '@metamask/utils'; import { AssertionError, stringToBytes } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import fetchMock from 'jest-fetch-mock'; -import { createAsyncMiddleware, JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { pipeline } from 'stream'; import type { Duplex } from 'stream'; @@ -821,7 +824,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, - error: ethErrors.provider.userRejectedRequest().message, + error: providerErrors.userRejectedRequest().message, type: SNAP_APPROVAL_INSTALL, }, }), @@ -2052,7 +2055,7 @@ describe('SnapController', () => { }, }), ).rejects.toThrow( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: 'Invalid JSON-RPC request: At path: jsonrpc -- Expected the literal `"2.0"`, but received: "kaplar".', }), @@ -2657,7 +2660,7 @@ describe('SnapController', () => { id: expect.any(String), requestState: { loading: false, - error: ethErrors.provider.userRejectedRequest().message, + error: providerErrors.userRejectedRequest().message, type: SNAP_APPROVAL_INSTALL, }, }), @@ -3801,7 +3804,7 @@ describe('SnapController', () => { detectSnapLocation(), ), ).rejects.toThrow( - ethErrors.rpc.invalidParams( + rpcErrors.invalidParams( `Snap "${MOCK_SNAP_ID}@${snap.version}" is already installed. Couldn't update to a version inside requested "*" range.`, ), ); diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 55423f5317..3ea38f159e 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -24,6 +24,7 @@ import type { ValidPermission, } from '@metamask/permission-controller'; import { SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { BlockReason } from '@metamask/snaps-registry'; import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods'; import type { @@ -77,7 +78,6 @@ import { } from '@metamask/utils'; import type { StateMachine } from '@xstate/fsm'; import { createMachine, interpret } from '@xstate/fsm'; -import { ethErrors } from 'eth-rpc-errors'; import type { Patch } from 'immer'; import { nanoid } from 'nanoid'; @@ -1683,7 +1683,7 @@ export class SnapController extends BaseController< const [error, version] = resolveVersionRange(rawVersion); if (error) { - throw ethErrors.rpc.invalidParams( + throw rpcErrors.invalidParams( `The "version" field must be a valid SemVer version range if specified. Received: "${rawVersion}".`, ); } @@ -1942,7 +1942,7 @@ export class SnapController extends BaseController< const newVersion = manifest.version; if (!gtVersion(newVersion, snap.version)) { - throw ethErrors.rpc.invalidParams( + throw rpcErrors.invalidParams( `Snap "${snapId}@${snap.version}" is already installed. Couldn't update to a version inside requested "${newVersionRange}" range.`, ); } diff --git a/packages/snaps-controllers/src/snaps/endowments/cronjob.ts b/packages/snaps-controllers/src/snaps/endowments/cronjob.ts index c243b9b658..fbe9957636 100644 --- a/packages/snaps-controllers/src/snaps/endowments/cronjob.ts +++ b/packages/snaps-controllers/src/snaps/endowments/cronjob.ts @@ -7,6 +7,7 @@ import type { CaveatSpecificationConstraint, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { CronjobSpecification } from '@metamask/snaps-utils'; import { SnapCaveatType, @@ -14,7 +15,6 @@ import { } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; import { assert, hasProperty, isPlainObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { SnapEndowments } from './enum'; @@ -109,7 +109,7 @@ export function getCronjobCaveatJobs( */ export function validateCronjobCaveat(caveat: Caveat) { if (!hasProperty(caveat, 'value') || !isPlainObject(caveat.value)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a plain object.', }); } @@ -117,13 +117,13 @@ export function validateCronjobCaveat(caveat: Caveat) { const { value } = caveat; if (!hasProperty(value, 'jobs') || !isPlainObject(value)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a plain object.', }); } if (!isCronjobSpecificationArray(value.jobs)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a valid cronjob specification array.', }); } diff --git a/packages/snaps-controllers/src/snaps/endowments/keyring.ts b/packages/snaps-controllers/src/snaps/endowments/keyring.ts index adde11ff40..903d2d094e 100644 --- a/packages/snaps-controllers/src/snaps/endowments/keyring.ts +++ b/packages/snaps-controllers/src/snaps/endowments/keyring.ts @@ -8,11 +8,11 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { KeyringOrigins } from '@metamask/snaps-utils'; import { assertIsKeyringOrigins, SnapCaveatType } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; import { assert, hasProperty, isPlainObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { SnapEndowments } from './enum'; @@ -49,7 +49,7 @@ const specificationBuilder: PermissionSpecificationBuilder< caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.KeyringOrigin ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.KeyringOrigin}" caveat.`, }); } @@ -72,13 +72,13 @@ export const keyringEndowmentBuilder = Object.freeze({ */ function validateCaveatOrigins(caveat: Caveat) { if (!hasProperty(caveat, 'value') || !isPlainObject(caveat.value)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Invalid keyring origins: Expected a plain object.', }); } const { value } = caveat; - assertIsKeyringOrigins(value, ethErrors.rpc.invalidParams); + assertIsKeyringOrigins(value, rpcErrors.invalidParams); } /** diff --git a/packages/snaps-controllers/src/snaps/endowments/name-lookup.ts b/packages/snaps-controllers/src/snaps/endowments/name-lookup.ts index 1f9716dca1..be9f605f7b 100644 --- a/packages/snaps-controllers/src/snaps/endowments/name-lookup.ts +++ b/packages/snaps-controllers/src/snaps/endowments/name-lookup.ts @@ -8,10 +8,10 @@ import type { PermissionConstraint, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { SnapCaveatType, isChainId } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; import { assert, hasProperty, isPlainObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { SnapEndowments } from './enum'; @@ -48,7 +48,7 @@ const specificationBuilder: PermissionSpecificationBuilder< (caveats !== null && caveats?.length > 1) || (caveats?.length === 1 && caveats[0].type !== SnapCaveatType.ChainIds) ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.ChainIds}" caveat.`, }); } @@ -70,7 +70,7 @@ export const nameLookupEndowmentBuilder = Object.freeze({ */ function validateCaveat(caveat: Caveat): void { if (!hasProperty(caveat, 'value') || !isPlainObject(caveat)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a plain object.', }); } diff --git a/packages/snaps-controllers/src/snaps/endowments/rpc.ts b/packages/snaps-controllers/src/snaps/endowments/rpc.ts index 6ebc6c0285..2892a00b9b 100644 --- a/packages/snaps-controllers/src/snaps/endowments/rpc.ts +++ b/packages/snaps-controllers/src/snaps/endowments/rpc.ts @@ -8,11 +8,11 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { RpcOrigins } from '@metamask/snaps-utils'; import { assertIsRpcOrigins, SnapCaveatType } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; import { hasProperty, isPlainObject, assert } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { SnapEndowments } from './enum'; @@ -52,7 +52,7 @@ const specificationBuilder: PermissionSpecificationBuilder< caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.RpcOrigin ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.RpcOrigin}" caveat.`, }); } @@ -75,13 +75,13 @@ export const rpcEndowmentBuilder = Object.freeze({ */ function validateCaveatOrigins(caveat: Caveat) { if (!hasProperty(caveat, 'value') || !isPlainObject(caveat.value)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Invalid JSON-RPC origins: Expected a plain object.', }); } const { value } = caveat; - assertIsRpcOrigins(value, ethErrors.rpc.invalidParams); + assertIsRpcOrigins(value, rpcErrors.invalidParams); } /** diff --git a/packages/snaps-controllers/src/snaps/endowments/transaction-insight.ts b/packages/snaps-controllers/src/snaps/endowments/transaction-insight.ts index cc0b37e90d..fe500c5ee3 100644 --- a/packages/snaps-controllers/src/snaps/endowments/transaction-insight.ts +++ b/packages/snaps-controllers/src/snaps/endowments/transaction-insight.ts @@ -8,10 +8,10 @@ import type { Caveat, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { SnapCaveatType } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; import { assert, hasProperty, isObject, isPlainObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { SnapEndowments } from './enum'; @@ -48,7 +48,7 @@ const specificationBuilder: PermissionSpecificationBuilder< (caveats?.length === 1 && caveats[0].type !== SnapCaveatType.TransactionOrigin) ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.TransactionOrigin}" caveat.`, }); } @@ -70,7 +70,7 @@ export const transactionInsightEndowmentBuilder = Object.freeze({ */ function validateCaveat(caveat: Caveat): void { if (!hasProperty(caveat, 'value') || !isPlainObject(caveat)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a plain object.', }); } diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index dab21154ed..b2630dce3e 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -7,6 +7,7 @@ import type { SubjectMetadata, } from '@metamask/permission-controller'; import { SubjectType } from '@metamask/permission-controller'; +import { providerErrors } from '@metamask/rpc-errors'; import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods'; import type { ValidatedSnapId } from '@metamask/snaps-utils'; import { SnapCaveatType } from '@metamask/snaps-utils'; @@ -19,7 +20,6 @@ import { MOCK_SNAP_ID, } from '@metamask/snaps-utils/test-utils'; import type { Json } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { CronjobControllerActions, @@ -88,7 +88,7 @@ export class MockApprovalController { }) { if (this.#approval) { if (requestState.loading === false && !requestState.error) { - this.#approval.promise.reject(ethErrors.provider.userRejectedRequest()); + this.#approval.promise.reject(providerErrors.userRejectedRequest()); } } } diff --git a/packages/snaps-controllers/src/test-utils/execution-environment.ts b/packages/snaps-controllers/src/test-utils/execution-environment.ts index 6cb2842c3b..5f832a6dde 100644 --- a/packages/snaps-controllers/src/test-utils/execution-environment.ts +++ b/packages/snaps-controllers/src/test-utils/execution-environment.ts @@ -1,6 +1,6 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { logError, type SnapRpcHookArgs } from '@metamask/snaps-utils'; import type { MockControllerMessenger } from '@metamask/snaps-utils/test-utils'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { pipeline } from 'stream'; diff --git a/packages/snaps-controllers/src/test-utils/service.ts b/packages/snaps-controllers/src/test-utils/service.ts index caf144fb7a..3db9fcc4cf 100644 --- a/packages/snaps-controllers/src/test-utils/service.ts +++ b/packages/snaps-controllers/src/test-utils/service.ts @@ -1,6 +1,6 @@ import { ControllerMessenger } from '@metamask/base-controller'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { logError } from '@metamask/snaps-utils'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { pipeline } from 'stream'; import type { Duplex } from 'stream'; diff --git a/packages/snaps-execution-environments/coverage.json b/packages/snaps-execution-environments/coverage.json index 3955b04bb7..34fcf6a6bc 100644 --- a/packages/snaps-execution-environments/coverage.json +++ b/packages/snaps-execution-environments/coverage.json @@ -1,6 +1,6 @@ { - "branches": 80.41, + "branches": 79.72, "functions": 91.91, - "lines": 91.41, - "statements": 91.12 + "lines": 91.08, + "statements": 90.8 } diff --git a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json index d9b02abbef..efe12950d0 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json @@ -11,6 +11,13 @@ "browserify>string_decoder": true } }, + "@metamask/json-rpc-engine": { + "packages": { + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true + } + }, "@metamask/object-multiplex": { "globals": { "console.warn": true @@ -100,16 +107,16 @@ "web3": true }, "packages": { + "@metamask/json-rpc-engine": true, "@metamask/object-multiplex": true, "@metamask/providers>@metamask/safe-event-emitter": true, "@metamask/providers>detect-browser": true, "@metamask/providers>extension-port-stream": true, "@metamask/providers>is-stream": true, "@metamask/providers>json-rpc-middleware-stream": true, - "@metamask/providers>pump": true, - "eslint>fast-deep-equal": true, - "eth-rpc-errors": true, - "json-rpc-engine": true + "@metamask/rpc-errors": true, + "browserify>stream-browserify": true, + "eslint>fast-deep-equal": true } }, "@metamask/providers>@metamask/safe-event-emitter": { @@ -141,8 +148,8 @@ "setTimeout": true }, "packages": { - "@metamask/providers>json-rpc-middleware-stream>readable-stream": true, - "json-rpc-engine>@metamask/safe-event-emitter": true + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/providers>json-rpc-middleware-stream>readable-stream": true } }, "@metamask/providers>json-rpc-middleware-stream>readable-stream": { @@ -170,12 +177,10 @@ "@metamask/providers>json-rpc-middleware-stream>readable-stream>safe-buffer": true } }, - "@metamask/providers>pump": { + "@metamask/rpc-errors": { "packages": { - "@metamask/object-multiplex>end-of-stream": true, - "@metamask/object-multiplex>once": true, - "browserify>browser-resolve": true, - "browserify>process": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/utils": { @@ -289,11 +294,6 @@ "eslint>debug>ms": true } }, - "eth-rpc-errors": { - "packages": { - "eth-rpc-errors>fast-safe-stringify": true - } - }, "external:../snaps-utils/src/errors.ts": { "packages": { "@metamask/utils": true @@ -338,20 +338,6 @@ "superstruct": true } }, - "json-rpc-engine": { - "packages": { - "eth-rpc-errors": true, - "json-rpc-engine>@metamask/safe-event-emitter": true - } - }, - "json-rpc-engine>@metamask/safe-event-emitter": { - "globals": { - "setTimeout": true - }, - "packages": { - "browserify>events": true - } - }, "superstruct": { "globals": { "console.warn": true, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json index 009e55468c..e94fb225fa 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json @@ -23,6 +23,13 @@ "util": true } }, + "@metamask/json-rpc-engine": { + "packages": { + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true + } + }, "@metamask/object-multiplex": { "globals": { "console.warn": true @@ -126,6 +133,9 @@ } }, "@metamask/providers": { + "builtin": { + "stream.pipeline": true + }, "globals": { "Event": true, "addEventListener": true, @@ -140,16 +150,16 @@ "web3": true }, "packages": { + "@metamask/json-rpc-engine": true, "@metamask/object-multiplex": true, "@metamask/providers>@metamask/safe-event-emitter": true, "@metamask/providers>detect-browser": true, "@metamask/providers>extension-port-stream": true, "@metamask/providers>is-stream": true, "@metamask/providers>json-rpc-middleware-stream": true, - "@metamask/providers>pump": true, + "@metamask/rpc-errors": true, "eslint>fast-deep-equal": true, - "eth-rpc-errors": true, - "json-rpc-engine": true + "stream": true } }, "@metamask/providers>@metamask/safe-event-emitter": { @@ -186,8 +196,8 @@ "setTimeout": true }, "packages": { - "@metamask/providers>json-rpc-middleware-stream>readable-stream": true, - "json-rpc-engine>@metamask/safe-event-emitter": true + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/providers>json-rpc-middleware-stream>readable-stream": true } }, "@metamask/providers>json-rpc-middleware-stream>readable-stream": { @@ -231,17 +241,10 @@ "@metamask/providers>json-rpc-middleware-stream>readable-stream>safe-buffer": true } }, - "@metamask/providers>pump": { - "builtin": { - "fs": true - }, - "globals": { - "process.version": true - }, + "@metamask/rpc-errors": { "packages": { - "@metamask/object-multiplex>end-of-stream": true, - "@metamask/object-multiplex>once": true, - "fs": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/utils": { @@ -355,11 +358,6 @@ "util": true } }, - "eth-rpc-errors": { - "packages": { - "eth-rpc-errors>fast-safe-stringify": true - } - }, "external:../snaps-utils/src/errors.ts": { "packages": { "@metamask/utils": true @@ -409,23 +407,6 @@ "process.argv": true } }, - "json-rpc-engine": { - "packages": { - "eth-rpc-errors": true, - "json-rpc-engine>@metamask/safe-event-emitter": true - } - }, - "json-rpc-engine>@metamask/safe-event-emitter": { - "builtin": { - "events.EventEmitter": true - }, - "globals": { - "setTimeout": true - }, - "packages": { - "events": true - } - }, "superstruct": { "globals": { "console.warn": true, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json index 009e55468c..e94fb225fa 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json @@ -23,6 +23,13 @@ "util": true } }, + "@metamask/json-rpc-engine": { + "packages": { + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true + } + }, "@metamask/object-multiplex": { "globals": { "console.warn": true @@ -126,6 +133,9 @@ } }, "@metamask/providers": { + "builtin": { + "stream.pipeline": true + }, "globals": { "Event": true, "addEventListener": true, @@ -140,16 +150,16 @@ "web3": true }, "packages": { + "@metamask/json-rpc-engine": true, "@metamask/object-multiplex": true, "@metamask/providers>@metamask/safe-event-emitter": true, "@metamask/providers>detect-browser": true, "@metamask/providers>extension-port-stream": true, "@metamask/providers>is-stream": true, "@metamask/providers>json-rpc-middleware-stream": true, - "@metamask/providers>pump": true, + "@metamask/rpc-errors": true, "eslint>fast-deep-equal": true, - "eth-rpc-errors": true, - "json-rpc-engine": true + "stream": true } }, "@metamask/providers>@metamask/safe-event-emitter": { @@ -186,8 +196,8 @@ "setTimeout": true }, "packages": { - "@metamask/providers>json-rpc-middleware-stream>readable-stream": true, - "json-rpc-engine>@metamask/safe-event-emitter": true + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/providers>json-rpc-middleware-stream>readable-stream": true } }, "@metamask/providers>json-rpc-middleware-stream>readable-stream": { @@ -231,17 +241,10 @@ "@metamask/providers>json-rpc-middleware-stream>readable-stream>safe-buffer": true } }, - "@metamask/providers>pump": { - "builtin": { - "fs": true - }, - "globals": { - "process.version": true - }, + "@metamask/rpc-errors": { "packages": { - "@metamask/object-multiplex>end-of-stream": true, - "@metamask/object-multiplex>once": true, - "fs": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/utils": { @@ -355,11 +358,6 @@ "util": true } }, - "eth-rpc-errors": { - "packages": { - "eth-rpc-errors>fast-safe-stringify": true - } - }, "external:../snaps-utils/src/errors.ts": { "packages": { "@metamask/utils": true @@ -409,23 +407,6 @@ "process.argv": true } }, - "json-rpc-engine": { - "packages": { - "eth-rpc-errors": true, - "json-rpc-engine>@metamask/safe-event-emitter": true - } - }, - "json-rpc-engine>@metamask/safe-event-emitter": { - "builtin": { - "events.EventEmitter": true - }, - "globals": { - "setTimeout": true - }, - "packages": { - "events": true - } - }, "superstruct": { "globals": { "console.warn": true, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json index d9b02abbef..efe12950d0 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json @@ -11,6 +11,13 @@ "browserify>string_decoder": true } }, + "@metamask/json-rpc-engine": { + "packages": { + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true + } + }, "@metamask/object-multiplex": { "globals": { "console.warn": true @@ -100,16 +107,16 @@ "web3": true }, "packages": { + "@metamask/json-rpc-engine": true, "@metamask/object-multiplex": true, "@metamask/providers>@metamask/safe-event-emitter": true, "@metamask/providers>detect-browser": true, "@metamask/providers>extension-port-stream": true, "@metamask/providers>is-stream": true, "@metamask/providers>json-rpc-middleware-stream": true, - "@metamask/providers>pump": true, - "eslint>fast-deep-equal": true, - "eth-rpc-errors": true, - "json-rpc-engine": true + "@metamask/rpc-errors": true, + "browserify>stream-browserify": true, + "eslint>fast-deep-equal": true } }, "@metamask/providers>@metamask/safe-event-emitter": { @@ -141,8 +148,8 @@ "setTimeout": true }, "packages": { - "@metamask/providers>json-rpc-middleware-stream>readable-stream": true, - "json-rpc-engine>@metamask/safe-event-emitter": true + "@metamask/providers>@metamask/safe-event-emitter": true, + "@metamask/providers>json-rpc-middleware-stream>readable-stream": true } }, "@metamask/providers>json-rpc-middleware-stream>readable-stream": { @@ -170,12 +177,10 @@ "@metamask/providers>json-rpc-middleware-stream>readable-stream>safe-buffer": true } }, - "@metamask/providers>pump": { + "@metamask/rpc-errors": { "packages": { - "@metamask/object-multiplex>end-of-stream": true, - "@metamask/object-multiplex>once": true, - "browserify>browser-resolve": true, - "browserify>process": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/utils": { @@ -289,11 +294,6 @@ "eslint>debug>ms": true } }, - "eth-rpc-errors": { - "packages": { - "eth-rpc-errors>fast-safe-stringify": true - } - }, "external:../snaps-utils/src/errors.ts": { "packages": { "@metamask/utils": true @@ -338,20 +338,6 @@ "superstruct": true } }, - "json-rpc-engine": { - "packages": { - "eth-rpc-errors": true, - "json-rpc-engine>@metamask/safe-event-emitter": true - } - }, - "json-rpc-engine>@metamask/safe-event-emitter": { - "globals": { - "setTimeout": true - }, - "packages": { - "browserify>events": true - } - }, "superstruct": { "globals": { "console.warn": true, diff --git a/packages/snaps-execution-environments/lavamoat/build-system/policy.json b/packages/snaps-execution-environments/lavamoat/build-system/policy.json index e07b75f5cc..e60ecf55fb 100644 --- a/packages/snaps-execution-environments/lavamoat/build-system/policy.json +++ b/packages/snaps-execution-environments/lavamoat/build-system/policy.json @@ -1734,7 +1734,7 @@ "Buffer.isBuffer": true }, "packages": { - "eth-rpc-errors>fast-safe-stringify": true + "@metamask/rpc-errors>fast-safe-stringify": true } }, "browserify>string_decoder": { diff --git a/packages/snaps-execution-environments/package.json b/packages/snaps-execution-environments/package.json index 7aad6bd118..7c89d4cdba 100644 --- a/packages/snaps-execution-environments/package.json +++ b/packages/snaps-execution-environments/package.json @@ -44,14 +44,14 @@ "lint:dependencies": "depcheck" }, "dependencies": { + "@metamask/json-rpc-engine": "^7.1.1", "@metamask/object-multiplex": "^1.2.0", "@metamask/post-message-stream": "^7.0.0", - "@metamask/providers": "^11.1.1", + "@metamask/providers": "^13.0.0", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-rpc-methods": "workspace:^", "@metamask/snaps-utils": "workspace:^", "@metamask/utils": "^8.1.0", - "eth-rpc-errors": "^4.0.3", - "json-rpc-engine": "^6.1.0", "nanoid": "^3.1.31", "superstruct": "^1.0.3" }, diff --git a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts index 44926cff3f..08739ff4c2 100644 --- a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts +++ b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts @@ -317,6 +317,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'snap_confirm', }, stack: expect.any(String), @@ -357,6 +358,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'wallet_requestSnaps', }, stack: expect.any(String), @@ -539,7 +541,8 @@ describe('BaseSnapExecutor', () => { jsonrpc: '2.0', error: { code: -32603, - message: expect.stringContaining('undefined'), + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', data: expect.any(Object), }, id: 2, @@ -654,6 +657,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'wallet_requestSnaps', }, stack: expect.any(String), @@ -694,6 +698,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'snap_dialog', }, stack: expect.anything(), @@ -749,6 +754,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'wallet_requestSnaps', }, stack: expect.any(String), @@ -804,6 +810,7 @@ describe('BaseSnapExecutor', () => { code: -32601, message: 'The method does not exist / is not available.', data: { + cause: null, method: 'wallet_requestSnaps', }, stack: expect.any(String), @@ -1016,7 +1023,8 @@ describe('BaseSnapExecutor', () => { stack: error.stack, snapId: MOCK_SNAP_ID, }, - message: error.message, + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }, }, }); @@ -1076,7 +1084,8 @@ describe('BaseSnapExecutor', () => { stack: error.stack, snapId: MOCK_SNAP_ID, }, - message: error.message, + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }, }, }); @@ -1382,7 +1391,8 @@ describe('BaseSnapExecutor', () => { error: { code: -32603, data: expect.anything(), - message: expect.stringContaining('undefined'), + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }, }); @@ -1738,7 +1748,8 @@ describe('BaseSnapExecutor', () => { error: { code: -32603, data: expect.any(Object), - message: 'JSON-RPC responses must be JSON serializable objects.', + // TODO: Unwrap errors, and change this to the actual error message. + message: 'Execution Environment Error', }, }); }); diff --git a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts index 471b780b91..577a647f2c 100644 --- a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts +++ b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts @@ -1,7 +1,9 @@ // eslint-disable-next-line @typescript-eslint/triple-slash-reference, spaced-comment /// +import { createIdRemapMiddleware } from '@metamask/json-rpc-engine'; import { StreamProvider } from '@metamask/providers'; import type { RequestArguments } from '@metamask/providers/dist/BaseProvider'; +import { errorCodes, rpcErrors, serializeError } from '@metamask/rpc-errors'; import type { SnapsGlobalObject } from '@metamask/snaps-rpc-methods'; import type { SnapExports, @@ -28,8 +30,6 @@ import { hasProperty, getSafeJson, } from '@metamask/utils'; -import { errorCodes, ethErrors, serializeError } from 'eth-rpc-errors'; -import { createIdRemapMiddleware } from 'json-rpc-engine'; import type { Duplex } from 'stream'; import { validate } from 'superstruct'; @@ -141,7 +141,7 @@ export class BaseSnapExecutor { assert( !required || handler !== undefined, `No ${handlerType} handler exported for snap "${target}`, - ethErrors.rpc.methodNotSupported, + rpcErrors.methodNotSupported, ); // Certain handlers are not required. If they are not exported, we @@ -164,7 +164,7 @@ export class BaseSnapExecutor { try { return getSafeJson(result); } catch (error) { - throw ethErrors.rpc.internal( + throw rpcErrors.internal( `Received non-JSON-serializable value: ${error.message.replace( /^Assertion failed: /u, '', @@ -199,7 +199,7 @@ export class BaseSnapExecutor { private async onCommandRequest(message: JsonRpcRequest) { if (!isJsonRpcRequest(message)) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: 'Command stream received a non-JSON-RPC request.', data: message, }); @@ -209,7 +209,7 @@ export class BaseSnapExecutor { if (!hasProperty(EXECUTION_ENVIRONMENT_METHODS, method)) { this.respond(id, { - error: ethErrors.rpc + error: rpcErrors .methodNotFound({ data: { method, @@ -228,7 +228,7 @@ export class BaseSnapExecutor { const [error] = validate(paramsAsArray, methodObject.struct); if (error) { this.respond(id, { - error: ethErrors.rpc + error: rpcErrors .invalidParams({ message: `Invalid parameters for method "${method}": ${error.message}.`, data: { @@ -255,7 +255,7 @@ export class BaseSnapExecutor { protected notify(requestObject: Omit) { if (!isValidJson(requestObject) || !isObject(requestObject)) { - throw ethErrors.rpc.internal( + throw rpcErrors.internal( 'JSON-RPC notifications must be JSON serializable objects', ); } @@ -373,7 +373,7 @@ export class BaseSnapExecutor { }); } catch (error) { this.removeSnap(snapId); - throw ethErrors.rpc.internal({ + throw rpcErrors.internal({ message: `Error while running snap '${snapId}': ${getErrorMessage( error, )}`, @@ -509,7 +509,7 @@ export class BaseSnapExecutor { ): Promise { const data = this.snapData.get(snapId); if (data === undefined) { - throw ethErrors.rpc.internal( + throw rpcErrors.internal( `Tried to execute in context of unknown snap: "${snapId}".`, ); } @@ -520,7 +520,7 @@ export class BaseSnapExecutor { (stop = () => reject( // TODO(rekmarks): Specify / standardize error code for this case. - ethErrors.rpc.internal( + rpcErrors.internal( `The snap "${snapId}" has been terminated during execution.`, ), )), diff --git a/packages/snaps-execution-environments/src/common/endowments/index.ts b/packages/snaps-execution-environments/src/common/endowments/index.ts index b5ff70416d..24783adf5c 100644 --- a/packages/snaps-execution-environments/src/common/endowments/index.ts +++ b/packages/snaps-execution-environments/src/common/endowments/index.ts @@ -1,9 +1,9 @@ import type { StreamProvider } from '@metamask/providers'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { SnapsGlobalObject } from '@metamask/snaps-rpc-methods'; import type { SnapId } from '@metamask/snaps-utils'; import { logWarning } from '@metamask/snaps-utils'; import { hasProperty } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { rootRealmGlobal } from '../globalObject'; import type { EndowmentFactoryOptions } from './commonEndowmentFactory'; @@ -102,7 +102,7 @@ export function createEndowments( } else { // If we get to this point, we've been passed an endowment that doesn't // exist in our current environment. - throw ethErrors.rpc.internal(`Unknown endowment: "${endowmentName}".`); + throw rpcErrors.internal(`Unknown endowment: "${endowmentName}".`); } return { allEndowments, teardowns }; }, diff --git a/packages/snaps-execution-environments/src/common/endowments/interval.ts b/packages/snaps-execution-environments/src/common/endowments/interval.ts index dba1988a76..635055dfb5 100644 --- a/packages/snaps-execution-environments/src/common/endowments/interval.ts +++ b/packages/snaps-execution-environments/src/common/endowments/interval.ts @@ -1,4 +1,4 @@ -import { ethErrors } from 'eth-rpc-errors'; +import { rpcErrors } from '@metamask/rpc-errors'; const MINIMUM_INTERVAL = 10; @@ -17,7 +17,7 @@ const createInterval = () => { const _setInterval = (handler: TimerHandler, timeout?: number): unknown => { if (typeof handler !== 'function') { - throw ethErrors.rpc.invalidInput( + throw rpcErrors.invalidInput( `The interval handler must be a function. Received: ${typeof handler}.`, ); } diff --git a/packages/snaps-execution-environments/src/common/endowments/timeout.ts b/packages/snaps-execution-environments/src/common/endowments/timeout.ts index 873019ba1d..9e008a9f35 100644 --- a/packages/snaps-execution-environments/src/common/endowments/timeout.ts +++ b/packages/snaps-execution-environments/src/common/endowments/timeout.ts @@ -1,4 +1,4 @@ -import { ethErrors } from 'eth-rpc-errors'; +import { rpcErrors } from '@metamask/rpc-errors'; const MINIMUM_TIMEOUT = 10; @@ -16,7 +16,7 @@ const createTimeout = () => { const registeredHandles = new Map(); const _setTimeout = (handler: TimerHandler, timeout?: number): unknown => { if (typeof handler !== 'function') { - throw ethErrors.rpc.internal( + throw rpcErrors.internal( `The timeout handler must be a function. Received: ${typeof handler}.`, ); } diff --git a/packages/snaps-execution-environments/src/common/globalEvents.ts b/packages/snaps-execution-environments/src/common/globalEvents.ts index 2eb9faa905..8d7d09ddcd 100644 --- a/packages/snaps-execution-environments/src/common/globalEvents.ts +++ b/packages/snaps-execution-environments/src/common/globalEvents.ts @@ -1,4 +1,4 @@ -import { ethErrors } from 'eth-rpc-errors'; +import { rpcErrors } from '@metamask/rpc-errors'; import { rootRealmGlobal } from './globalObject'; @@ -29,7 +29,7 @@ export function addEventListener( return rootRealmGlobal.process.on(event, listener); } - throw ethErrors.rpc.internal('Platform agnostic addEventListener failed.'); + throw rpcErrors.internal('Platform agnostic addEventListener failed.'); } /** diff --git a/packages/snaps-execution-environments/src/common/test-utils/endowments.ts b/packages/snaps-execution-environments/src/common/test-utils/endowments.ts index 7e4db25ec7..a8c1ce5303 100644 --- a/packages/snaps-execution-environments/src/common/test-utils/endowments.ts +++ b/packages/snaps-execution-environments/src/common/test-utils/endowments.ts @@ -1,10 +1,10 @@ // @ts-expect-error Walker has no types yet. import LavaTube from '@lavamoat/lavatube'; +import { createIdRemapMiddleware } from '@metamask/json-rpc-engine'; import ObjectMultiplex from '@metamask/object-multiplex'; import { StreamProvider } from '@metamask/providers'; import type { RequestArguments } from '@metamask/providers/dist/BaseProvider'; import { SNAP_STREAM_NAMES } from '@metamask/snaps-utils'; -import { createIdRemapMiddleware } from 'json-rpc-engine'; import { assertEthereumOutboundRequest, diff --git a/packages/snaps-execution-environments/src/common/utils.ts b/packages/snaps-execution-environments/src/common/utils.ts index 52cd380887..c5f3ea77e5 100644 --- a/packages/snaps-execution-environments/src/common/utils.ts +++ b/packages/snaps-execution-environments/src/common/utils.ts @@ -1,7 +1,7 @@ import type { StreamProvider } from '@metamask/providers'; import type { RequestArguments } from '@metamask/providers/dist/BaseProvider'; +import { rpcErrors } from '@metamask/rpc-errors'; import { assert, assertStruct, getSafeJson, JsonStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { log } from '../logging'; @@ -133,11 +133,11 @@ export function assertSnapOutboundRequest(args: RequestArguments) { String.prototype.startsWith.call(args.method, 'wallet_') || String.prototype.startsWith.call(args.method, 'snap_'), 'The global Snap API only allows RPC methods starting with `wallet_*` and `snap_*`.', - ethErrors.rpc.methodNotSupported, + rpcErrors.methodNotSupported, ); assert( !BLOCKED_RPC_METHODS.includes(args.method), - ethErrors.rpc.methodNotFound({ + rpcErrors.methodNotFound({ data: { method: args.method, }, @@ -147,7 +147,7 @@ export function assertSnapOutboundRequest(args: RequestArguments) { args, JsonStruct, 'Provided value is not JSON-RPC compatible', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } @@ -160,7 +160,7 @@ export function assertEthereumOutboundRequest(args: RequestArguments) { // Disallow snaps methods for separation of concerns. assert( !String.prototype.startsWith.call(args.method, 'snap_'), - ethErrors.rpc.methodNotFound({ + rpcErrors.methodNotFound({ data: { method: args.method, }, @@ -168,7 +168,7 @@ export function assertEthereumOutboundRequest(args: RequestArguments) { ); assert( !BLOCKED_RPC_METHODS.includes(args.method), - ethErrors.rpc.methodNotFound({ + rpcErrors.methodNotFound({ data: { method: args.method, }, @@ -178,7 +178,7 @@ export function assertEthereumOutboundRequest(args: RequestArguments) { args, JsonStruct, 'Provided value is not JSON-RPC compatible', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } diff --git a/packages/snaps-execution-environments/src/common/validation.ts b/packages/snaps-execution-environments/src/common/validation.ts index d929705e68..13e4c7effa 100644 --- a/packages/snaps-execution-environments/src/common/validation.ts +++ b/packages/snaps-execution-environments/src/common/validation.ts @@ -1,3 +1,4 @@ +import { rpcErrors } from '@metamask/rpc-errors'; import { ChainIdStruct, HandlerType } from '@metamask/snaps-utils'; import type { Json, JsonRpcSuccess } from '@metamask/utils'; import { @@ -7,7 +8,6 @@ import { JsonRpcSuccessStruct, JsonStruct, } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { Infer } from 'superstruct'; import { array, @@ -133,7 +133,7 @@ export function assertIsOnTransactionRequestArguments( value, OnTransactionRequestArgumentsStruct, 'Invalid request params', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } @@ -176,7 +176,7 @@ export function assertIsOnNameLookupRequestArguments( value, OnNameLookupRequestArgumentsStruct, 'Invalid request params', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } diff --git a/packages/snaps-rpc-methods/jest.config.js b/packages/snaps-rpc-methods/jest.config.js index 513d9b4336..9e3fcf18d1 100644 --- a/packages/snaps-rpc-methods/jest.config.js +++ b/packages/snaps-rpc-methods/jest.config.js @@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, { ], coverageThreshold: { global: { - branches: 87.6, + branches: 87.8, functions: 100, - lines: 97.95, - statements: 96.34, + lines: 98.17, + statements: 96.54, }, }, }); diff --git a/packages/snaps-rpc-methods/package.json b/packages/snaps-rpc-methods/package.json index 6bb5bd6737..3605510cea 100644 --- a/packages/snaps-rpc-methods/package.json +++ b/packages/snaps-rpc-methods/package.json @@ -37,13 +37,12 @@ }, "dependencies": { "@metamask/key-tree": "^9.0.0", - "@metamask/permission-controller": "^4.1.2", + "@metamask/permission-controller": "^5.0.0", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-ui": "workspace:^", "@metamask/snaps-utils": "workspace:^", - "@metamask/types": "^1.1.0", "@metamask/utils": "^8.1.0", "@noble/hashes": "^1.3.1", - "eth-rpc-errors": "^4.0.3", "superstruct": "^1.0.3" }, "devDependencies": { @@ -54,6 +53,7 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", + "@metamask/json-rpc-engine": "^7.1.1", "@swc/cli": "^0.1.62", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", @@ -73,7 +73,6 @@ "expect-type": "^0.15.0", "jest": "^29.0.2", "jest-it-up": "^2.0.0", - "json-rpc-engine": "^6.1.0", "prettier": "^2.7.1", "prettier-plugin-packagejson": "^2.2.11", "rimraf": "^4.1.2", diff --git a/packages/snaps-rpc-methods/src/permitted/common/snapInstallation.ts b/packages/snaps-rpc-methods/src/permitted/common/snapInstallation.ts index 1015fa8a32..d7cbd5dd55 100644 --- a/packages/snaps-rpc-methods/src/permitted/common/snapInstallation.ts +++ b/packages/snaps-rpc-methods/src/permitted/common/snapInstallation.ts @@ -1,7 +1,7 @@ import type { RequestedPermissions } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { InstallSnapsResult } from '@metamask/snaps-utils'; import { isObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; export type { InstallSnapsResult } from '@metamask/snaps-utils'; @@ -25,12 +25,12 @@ export async function handleInstallSnaps( installSnaps: InstallSnapsHook, ): Promise { if (!isObject(requestedSnaps)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Invalid snap installation params.`, data: { requestedSnaps }, }); } else if (Object.keys(requestedSnaps).length === 0) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Must specify at least one snap to install.`, data: { requestedSnaps }, }); diff --git a/packages/snaps-rpc-methods/src/permitted/getFile.test.ts b/packages/snaps-rpc-methods/src/permitted/getFile.test.ts index ba7db7d57e..234bac0d07 100644 --- a/packages/snaps-rpc-methods/src/permitted/getFile.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/getFile.test.ts @@ -1,14 +1,14 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { AuxiliaryFileEncoding, VirtualFile } from '@metamask/snaps-utils'; import type { JsonRpcRequest, PendingJsonRpcResponse, JsonRpcFailure, JsonRpcSuccess, -} from '@metamask/types'; +} from '@metamask/utils'; import { stringToBytes } from '@metamask/utils'; -import { JsonRpcEngine } from 'json-rpc-engine'; -import type { GetFileHooks } from './getFile'; +import type { GetFileHooks, GetFileArgs } from './getFile'; import { getFileHandler } from './getFile'; describe('snap_getFile', () => { @@ -46,7 +46,7 @@ describe('snap_getFile', () => { const engine = new JsonRpcEngine(); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, + req as JsonRpcRequest, res as PendingJsonRpcResponse, next, end, @@ -88,7 +88,7 @@ describe('snap_getFile', () => { const engine = new JsonRpcEngine(); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, + req as JsonRpcRequest, res as PendingJsonRpcResponse, next, end, @@ -129,7 +129,7 @@ describe('snap_getFile', () => { const engine = new JsonRpcEngine(); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, + req as JsonRpcRequest, res as PendingJsonRpcResponse, next, end, @@ -148,7 +148,17 @@ describe('snap_getFile', () => { }, })) as JsonRpcFailure; - expect(response.error.message).toBe('foo bar'); + expect(response.error).toStrictEqual({ + code: -32603, + message: 'Internal JSON-RPC error.', + data: { + cause: { + message: 'foo bar', + stack: expect.any(String), + }, + }, + }); + expect(hooks.getSnapFile).toHaveBeenCalledWith( './src/foo.json', AuxiliaryFileEncoding.Base64, diff --git a/packages/snaps-rpc-methods/src/permitted/getFile.ts b/packages/snaps-rpc-methods/src/permitted/getFile.ts index 221be8a153..b6dd68de79 100644 --- a/packages/snaps-rpc-methods/src/permitted/getFile.ts +++ b/packages/snaps-rpc-methods/src/permitted/getFile.ts @@ -1,12 +1,13 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { enumValue, AuxiliaryFileEncoding } from '@metamask/snaps-utils'; import type { - PermittedHandlerExport, PendingJsonRpcResponse, - JsonRpcEngineEndCallback, JsonRpcRequest, -} from '@metamask/types'; + Json, +} from '@metamask/utils'; import { assertStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { Infer } from 'superstruct'; import { object, optional, string, union } from 'superstruct'; @@ -60,7 +61,7 @@ export type GetFileHooks = { */ async function implementation( req: JsonRpcRequest, - res: PendingJsonRpcResponse, + res: PendingJsonRpcResponse, _next: unknown, end: JsonRpcEngineEndCallback, { getSnapFile }: GetFileHooks, @@ -71,7 +72,7 @@ async function implementation( params, GetFileArgsStruct, 'Invalid "snap_getFile" parameters', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); try { diff --git a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts index a275aa3efe..0768e6e7c4 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts @@ -1,3 +1,5 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; +import { rpcErrors } from '@metamask/rpc-errors'; import { HandlerType } from '@metamask/snaps-utils'; import { MOCK_SNAP_ID, getSnapObject } from '@metamask/snaps-utils/test-utils'; import type { @@ -6,8 +8,6 @@ import type { JsonRpcFailure, JsonRpcSuccess, } from '@metamask/types'; -import { ethErrors } from 'eth-rpc-errors'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { invokeKeyringHandler } from './invokeKeyring'; @@ -93,7 +93,7 @@ describe('wallet_invokeKeyring', () => { hooks.hasPermission.mockImplementation(() => true); hooks.getSnap.mockImplementation(() => getSnapObject()); hooks.handleSnapRpcRequest.mockImplementation(() => { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: 'Failed to start snap.', }); }); @@ -124,7 +124,7 @@ describe('wallet_invokeKeyring', () => { })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidRequest({ message: 'Failed to start snap.', }) @@ -141,7 +141,7 @@ describe('wallet_invokeKeyring', () => { hooks.hasPermission.mockImplementation(() => true); hooks.getSnap.mockImplementation(() => getSnapObject()); hooks.handleSnapRpcRequest.mockImplementation(() => { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: 'Failed to start snap.', }); }); @@ -172,7 +172,7 @@ describe('wallet_invokeKeyring', () => { })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidRequest({ message: 'The origin "metamask.io" is not allowed to invoke the method "foo".', @@ -190,7 +190,7 @@ describe('wallet_invokeKeyring', () => { hooks.hasPermission.mockImplementation(() => true); hooks.getSnap.mockImplementation(() => getSnapObject()); hooks.handleSnapRpcRequest.mockImplementation(() => { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: 'Failed to start snap.', }); }); @@ -221,7 +221,7 @@ describe('wallet_invokeKeyring', () => { })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidRequest({ message: 'The request must have a method.' }) .serialize(), stack: expect.any(String), @@ -260,7 +260,7 @@ describe('wallet_invokeKeyring', () => { })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidRequest({ message: `The snap "${MOCK_SNAP_ID}" is not connected to "metamask.io". Please connect before invoking the snap.`, }) @@ -302,7 +302,7 @@ describe('wallet_invokeKeyring', () => { })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidRequest({ message: `The snap "${MOCK_SNAP_ID}" is not installed. Please install it first, before invoking the snap.`, }) @@ -334,13 +334,12 @@ describe('wallet_invokeKeyring', () => { id: 1, method: 'wallet_invokeKeyring', params: { - snapId: undefined, request: [], }, })) as JsonRpcFailure; expect(response.error).toStrictEqual({ - ...ethErrors.rpc + ...rpcErrors .invalidParams({ message: 'Must specify a valid snap ID.', }) diff --git a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts index 1b6ef046a4..a0a91286bb 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts @@ -1,3 +1,4 @@ +import { rpcErrors } from '@metamask/rpc-errors'; import type { Snap } from '@metamask/snaps-utils'; import { HandlerType, @@ -12,7 +13,6 @@ import type { JsonRpcRequest, } from '@metamask/types'; import { hasProperty, type Json } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; import type { InvokeSnapSugarArgs } from './invokeSnapSugar'; @@ -95,7 +95,7 @@ async function invokeKeyringImplementation( if (!origin || !hasPermission(origin, WALLET_SNAP_PERMISSION_KEY)) { return end( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: `The snap "${snapId}" is not connected to "${origin}". Please connect before invoking the snap.`, }), ); @@ -103,7 +103,7 @@ async function invokeKeyringImplementation( if (!getSnap(snapId)) { return end( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: `The snap "${snapId}" is not installed. Please install it first, before invoking the snap.`, }), ); @@ -111,7 +111,7 @@ async function invokeKeyringImplementation( if (!hasProperty(request, 'method') || typeof request.method !== 'string') { return end( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: 'The request must have a method.', }), ); @@ -120,7 +120,7 @@ async function invokeKeyringImplementation( const allowedMethods = getAllowedKeyringMethods(origin); if (!allowedMethods.includes(request.method)) { return end( - ethErrors.rpc.invalidRequest({ + rpcErrors.invalidRequest({ message: `The origin "${origin}" is not allowed to invoke the method "${request.method}".`, }), ); diff --git a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts index 916d8c7d2e..d1b751289a 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts @@ -1,9 +1,9 @@ +import { rpcErrors } from '@metamask/rpc-errors'; import type { JsonRpcEngineEndCallback, JsonRpcEngineNextCallback, JsonRpcRequest, } from '@metamask/types'; -import { ethErrors } from 'eth-rpc-errors'; import { getValidatedParams, invokeSnapSugar } from './invokeSnapSugar'; @@ -49,7 +49,7 @@ describe('wallet_invokeSnap', () => { invokeSnapSugar(req, _res, next, end); expect(end).toHaveBeenCalledWith( - ethErrors.rpc.invalidParams({ + rpcErrors.invalidParams({ message: 'Must specify a valid snap ID.', }), ); diff --git a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts index f382693512..7b040be79f 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts @@ -1,3 +1,4 @@ +import { rpcErrors } from '@metamask/rpc-errors'; import type { PermittedHandlerExport, JsonRpcRequest, @@ -5,7 +6,6 @@ import type { JsonRpcEngineEndCallback, } from '@metamask/types'; import { isObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; export type InvokeSnapSugarArgs = { snapId: string; @@ -64,7 +64,7 @@ export function invokeSnapSugar( */ export function getValidatedParams(params: unknown): InvokeSnapSugarArgs { if (!isObject(params)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected params to be a single object.', }); } @@ -72,13 +72,13 @@ export function getValidatedParams(params: unknown): InvokeSnapSugarArgs { const { snapId, request } = params; if (!snapId || typeof snapId !== 'string' || snapId === '') { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Must specify a valid snap ID.', }); } if (!isObject(request)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected request to be a single object.', }); } diff --git a/packages/snaps-rpc-methods/src/permitted/middleware.test.ts b/packages/snaps-rpc-methods/src/permitted/middleware.test.ts index f9b4abff5e..2e52dddf03 100644 --- a/packages/snaps-rpc-methods/src/permitted/middleware.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/middleware.test.ts @@ -1,8 +1,8 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { MOCK_SNAP_ID, getTruncatedSnap, } from '@metamask/snaps-utils/test-utils'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { createSnapsMethodMiddleware } from './middleware'; @@ -54,9 +54,9 @@ describe('createSnapsMethodMiddleware', () => { error: { code: -32603, data: { - originalError: {}, + cause: expect.objectContaining({ message: 'foo' }), }, - message: 'foo', + message: 'Internal JSON-RPC error.', }, }); }); @@ -82,6 +82,7 @@ describe('createSnapsMethodMiddleware', () => { error: { code: -32603, data: { + cause: null, request: { id: 1, jsonrpc: '2.0', diff --git a/packages/snaps-rpc-methods/src/permitted/middleware.ts b/packages/snaps-rpc-methods/src/permitted/middleware.ts index a1b76b64b2..dd88ab6c81 100644 --- a/packages/snaps-rpc-methods/src/permitted/middleware.ts +++ b/packages/snaps-rpc-methods/src/permitted/middleware.ts @@ -1,6 +1,7 @@ +import type { JsonRpcMiddleware } from '@metamask/json-rpc-engine'; +import { rpcErrors } from '@metamask/rpc-errors'; import { logError } from '@metamask/snaps-utils'; -import { ethErrors } from 'eth-rpc-errors'; -import type { JsonRpcMiddleware } from 'json-rpc-engine'; +import type { Json, JsonRpcParams } from '@metamask/utils'; import { selectHooks } from '../utils'; import { methodHandlers } from './handlers'; @@ -15,7 +16,7 @@ import { methodHandlers } from './handlers'; export function createSnapsMethodMiddleware( isSnap: boolean, hooks: Record, -): JsonRpcMiddleware { +): JsonRpcMiddleware { // This is not actually a misused promise, the type is just wrong // eslint-disable-next-line @typescript-eslint/no-misused-promises return async function methodMiddleware(request, response, next, end) { @@ -23,7 +24,7 @@ export function createSnapsMethodMiddleware( methodHandlers[request.method as keyof typeof methodHandlers]; if (handler) { if (request.method.startsWith('snap_') && !isSnap) { - return end(ethErrors.rpc.methodNotFound()); + return end(rpcErrors.methodNotFound()); } // TODO: Once json-rpc-engine types are up to date, we should type this correctly diff --git a/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts b/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts index 490001a84b..cf47821c00 100644 --- a/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts @@ -1,3 +1,4 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import type { RequestedPermissions, PermissionConstraint, @@ -15,7 +16,6 @@ import type { JsonRpcSuccess, PendingJsonRpcResponse, } from '@metamask/types'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { WALLET_SNAP_PERMISSION_KEY } from '../restricted/invokeSnap'; import { @@ -435,7 +435,11 @@ describe('implementation', () => { }); expect(response).toStrictEqual({ - error: { code: -32603, data: { originalError: {} }, message: 'error' }, + error: { + code: -32603, + data: { cause: expect.objectContaining({ message: 'error' }) }, + message: 'Internal JSON-RPC error.', + }, id: 1, jsonrpc: '2.0', }); diff --git a/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts b/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts index af818dfc05..085ea50063 100644 --- a/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts +++ b/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts @@ -3,6 +3,7 @@ import type { RequestedPermissions, Caveat, } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { SnapsPermissionRequest } from '@metamask/snaps-utils'; import { SnapCaveatType, @@ -16,7 +17,6 @@ import type { } from '@metamask/types'; import type { Json } from '@metamask/utils'; import { hasProperty, isObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { WALLET_SNAP_PERMISSION_KEY } from '../restricted/invokeSnap'; import type { MethodHooksObject } from '../utils'; @@ -177,7 +177,7 @@ async function requestSnapsImplementation( const requestedSnaps = req.params; if (!isObject(requestedSnaps)) { return end( - ethErrors.rpc.invalidParams({ + rpcErrors.invalidParams({ message: '"params" must be an object.', }), ); diff --git a/packages/snaps-rpc-methods/src/restricted/caveats/permittedCoinTypes.ts b/packages/snaps-rpc-methods/src/restricted/caveats/permittedCoinTypes.ts index b19aa7c3a7..628c37b32f 100644 --- a/packages/snaps-rpc-methods/src/restricted/caveats/permittedCoinTypes.ts +++ b/packages/snaps-rpc-methods/src/restricted/caveats/permittedCoinTypes.ts @@ -3,10 +3,10 @@ import type { RestrictedMethodCaveatSpecificationConstraint, Caveat, } from '@metamask/permission-controller'; +import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; import { FORBIDDEN_COIN_TYPES, SnapCaveatType } from '@metamask/snaps-utils'; import type { Json } from '@metamask/utils'; import { hasProperty, isPlainObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { GetBip44EntropyParams } from '../getBip44Entropy'; @@ -41,7 +41,7 @@ export function validateBIP44Params( value: unknown, ): asserts value is GetBip44EntropyParams { if (!isPlainObject(value) || !hasProperty(value, 'coinType')) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected a plain object containing a coin type.', }); } @@ -52,14 +52,14 @@ export function validateBIP44Params( value.coinType < 0 || value.coinType > 0x7fffffff ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Invalid "coinType" parameter. Coin type must be a non-negative integer.', }); } if (FORBIDDEN_COIN_TYPES.includes(value.coinType)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Coin type ${value.coinType} is forbidden.`, }); } @@ -78,7 +78,7 @@ export function validateBIP44Caveat(caveat: Caveat) { !Array.isArray(caveat.value) || caveat.value.length === 0 ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected non-empty array of coin types.', }); } @@ -108,7 +108,7 @@ export const PermittedCoinTypesCaveatSpecification: Record< ); if (!coinType) { - throw ethErrors.provider.unauthorized({ + throw providerErrors.unauthorized({ message: 'The requested coin type is not permitted. Allowed coin types must be specified in the snap manifest.', }); diff --git a/packages/snaps-rpc-methods/src/restricted/caveats/permittedDerivationPaths.ts b/packages/snaps-rpc-methods/src/restricted/caveats/permittedDerivationPaths.ts index be5f7b42af..8c4c76c81f 100644 --- a/packages/snaps-rpc-methods/src/restricted/caveats/permittedDerivationPaths.ts +++ b/packages/snaps-rpc-methods/src/restricted/caveats/permittedDerivationPaths.ts @@ -3,6 +3,7 @@ import type { PermissionConstraint, RestrictedMethodCaveatSpecificationConstraint, } from '@metamask/permission-controller'; +import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; import type { Bip32Entropy } from '@metamask/snaps-utils'; import { SnapCaveatType, @@ -11,7 +12,6 @@ import { } from '@metamask/snaps-utils'; import type { Json } from '@metamask/utils'; import { assertStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { array, size, type } from 'superstruct'; /** @@ -51,7 +51,7 @@ export function validateBIP32Path( value, Bip32EntropyStruct, 'Invalid BIP-32 entropy path definition', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } @@ -69,7 +69,7 @@ export function validateBIP32CaveatPaths( caveat, type({ value: size(array(Bip32EntropyStruct), 1, Infinity) }), 'Invalid BIP-32 entropy caveat', - ethErrors.rpc.internal, + rpcErrors.internal, ); } @@ -96,7 +96,7 @@ export const PermittedDerivationPathsCaveatSpecification: Record< ); if (!path) { - throw ethErrors.provider.unauthorized({ + throw providerErrors.unauthorized({ message: 'The requested path is not permitted. Allowed paths must be specified in the snap manifest.', }); diff --git a/packages/snaps-rpc-methods/src/restricted/caveats/snapIds.ts b/packages/snaps-rpc-methods/src/restricted/caveats/snapIds.ts index 734aeeb5db..e41f2cedc3 100644 --- a/packages/snaps-rpc-methods/src/restricted/caveats/snapIds.ts +++ b/packages/snaps-rpc-methods/src/restricted/caveats/snapIds.ts @@ -5,11 +5,11 @@ import type { RestrictedMethodCaveatSpecificationConstraint, PermissionConstraint, } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { SnapIds } from '@metamask/snaps-utils'; import { SnapCaveatType, SnapIdsStruct } from '@metamask/snaps-utils'; import type { Json } from '@metamask/utils'; import { hasProperty, assertStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { type } from 'superstruct'; import type { InvokeSnapParams } from '../invokeSnap'; @@ -50,7 +50,7 @@ export function validateSnapIdsCaveat( value: SnapIdsStruct, }), 'Expected caveat to have a value property of a non-empty object of snap IDs.', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); } diff --git a/packages/snaps-rpc-methods/src/restricted/dialog.ts b/packages/snaps-rpc-methods/src/restricted/dialog.ts index f8aeeb2d27..5e364bfe3b 100644 --- a/packages/snaps-rpc-methods/src/restricted/dialog.ts +++ b/packages/snaps-rpc-methods/src/restricted/dialog.ts @@ -4,12 +4,12 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { Component } from '@metamask/snaps-ui'; import { ComponentStruct } from '@metamask/snaps-ui'; import type { EnumToUnion } from '@metamask/snaps-utils'; import { enumValue } from '@metamask/snaps-utils'; import type { NonEmptyArray } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { Infer, Struct } from 'superstruct'; import { create, @@ -186,7 +186,7 @@ function getValidatedType(params: unknown): DialogType { try { return create(params, BaseParamsStruct).type; } catch (error) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `The "type" property must be one of: ${Object.values( DialogType, ).join(', ')}.`, @@ -213,18 +213,18 @@ function getValidatedParams( const { key, type: errorType } = error; if (key === 'placeholder' && errorType === 'never') { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Invalid params: Alerts or confirmations may not specify a "placeholder" field.', }); } - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Invalid params: ${error.message}.`, }); } /* istanbul ignore next */ - throw ethErrors.rpc.internal(); + throw rpcErrors.internal(); } } diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts index c2d661af1a..7c9d8b3297 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32Entropy.ts @@ -6,11 +6,11 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { Bip32Entropy } from '@metamask/snaps-utils'; import { SnapCaveatType } from '@metamask/snaps-utils'; import type { NonEmptyArray } from '@metamask/utils'; import { assert } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; import { getNode } from '../utils'; @@ -67,7 +67,7 @@ const specificationBuilder: PermissionSpecificationBuilder< caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.PermittedDerivationPaths ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.PermittedDerivationPaths}" caveat.`, }); } diff --git a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts index 07252c3233..f139e5795f 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip32PublicKey.ts @@ -5,6 +5,7 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { bip32entropy, Bip32PathStruct, @@ -12,7 +13,6 @@ import { } from '@metamask/snaps-utils'; import type { NonEmptyArray } from '@metamask/utils'; import { assertStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import { boolean, enums, object, optional } from 'superstruct'; import type { MethodHooksObject } from '../utils'; @@ -84,7 +84,7 @@ const specificationBuilder: PermissionSpecificationBuilder< caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.PermittedDerivationPaths ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.PermittedDerivationPaths}" caveat.`, }); } @@ -127,7 +127,7 @@ export function getBip32PublicKeyImplementation({ args.params, Bip32PublicKeyArgsStruct, 'Invalid BIP-32 public key params', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); const { params } = args; diff --git a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts index abd0da8639..7f411d8567 100644 --- a/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getBip44Entropy.ts @@ -7,9 +7,9 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { SnapCaveatType } from '@metamask/snaps-utils'; import type { NonEmptyArray } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; @@ -70,7 +70,7 @@ const specificationBuilder: PermissionSpecificationBuilder< caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.PermittedCoinTypes ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.PermittedCoinTypes}" caveat.`, }); } diff --git a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts index c967270fa4..71fdcf47d2 100644 --- a/packages/snaps-rpc-methods/src/restricted/getEntropy.ts +++ b/packages/snaps-rpc-methods/src/restricted/getEntropy.ts @@ -4,10 +4,10 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { SIP_6_MAGIC_VALUE } from '@metamask/snaps-utils'; import type { Hex, NonEmptyArray } from '@metamask/utils'; import { assertStruct } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { Infer } from 'superstruct'; import { literal, object, optional, string } from 'superstruct'; @@ -111,7 +111,7 @@ function getEntropyImplementation({ params, GetEntropyArgsStruct, 'Invalid "snap_getEntropy" parameters', - ethErrors.rpc.invalidParams, + rpcErrors.invalidParams, ); await getUnlockPromise(true); diff --git a/packages/snaps-rpc-methods/src/restricted/getLocale.ts b/packages/snaps-rpc-methods/src/restricted/getLocale.ts index 5ba1ef6940..42ba20482f 100644 --- a/packages/snaps-rpc-methods/src/restricted/getLocale.ts +++ b/packages/snaps-rpc-methods/src/restricted/getLocale.ts @@ -2,6 +2,7 @@ import type { PermissionSpecificationBuilder, ValidPermissionSpecification, RestrictedMethodOptions, + RestrictedMethodParameters, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; import type { NonEmptyArray } from '@metamask/utils'; @@ -68,7 +69,7 @@ export const getLocaleBuilder = Object.freeze({ */ export function getImplementation({ getLocale }: GetLocaleMethodHooks) { return async function implementation( - _args: RestrictedMethodOptions, + _args: RestrictedMethodOptions, ): Promise { return getLocale(); }; diff --git a/packages/snaps-rpc-methods/src/restricted/invokeSnap.ts b/packages/snaps-rpc-methods/src/restricted/invokeSnap.ts index a743309238..847a0134cb 100644 --- a/packages/snaps-rpc-methods/src/restricted/invokeSnap.ts +++ b/packages/snaps-rpc-methods/src/restricted/invokeSnap.ts @@ -6,6 +6,7 @@ import type { PermissionSideEffect, } from '@metamask/permission-controller'; import { PermissionType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { Snap, SnapId, @@ -15,7 +16,6 @@ import type { } from '@metamask/snaps-utils'; import { HandlerType, SnapCaveatType } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; @@ -127,7 +127,7 @@ const specificationBuilder: PermissionSpecificationBuilder< methodImplementation: getInvokeSnapImplementation(methodHooks), validator: ({ caveats }) => { if (caveats?.length !== 1 || caveats[0].type !== SnapCaveatType.SnapIds) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Expected a single "${SnapCaveatType.SnapIds}" caveat.`, }); } @@ -170,7 +170,7 @@ export function getInvokeSnapImplementation({ const { snapId, request } = params as InvokeSnapParams; if (!getSnap(snapId)) { - throw ethErrors.rpc.invalidRequest({ + throw rpcErrors.invalidRequest({ message: `The snap "${snapId}" is not installed. Please install it first, before invoking the snap.`, }); } diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts index 22ac50dbb0..e30d9e7f02 100644 --- a/packages/snaps-rpc-methods/src/restricted/manageState.test.ts +++ b/packages/snaps-rpc-methods/src/restricted/manageState.test.ts @@ -1,12 +1,12 @@ import { decrypt, encrypt } from '@metamask/browser-passworder'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import { MOCK_LOCAL_SNAP_ID, MOCK_SNAP_ID, TEST_SECRET_RECOVERY_PHRASE_BYTES, } from '@metamask/snaps-utils/test-utils'; import { webcrypto } from 'crypto'; -import { ethErrors } from 'eth-rpc-errors'; import { getManageStateImplementation, @@ -313,7 +313,7 @@ describe('snap_manageState', () => { params: { operation: ManageStateOperation.GetState }, }), ).rejects.toThrow( - ethErrors.rpc.internal({ + rpcErrors.internal({ message: 'Failed to decrypt snap state, the state must be corrupted.', }), ); diff --git a/packages/snaps-rpc-methods/src/restricted/manageState.ts b/packages/snaps-rpc-methods/src/restricted/manageState.ts index 5c0ea257d7..1260fdb0fa 100644 --- a/packages/snaps-rpc-methods/src/restricted/manageState.ts +++ b/packages/snaps-rpc-methods/src/restricted/manageState.ts @@ -4,11 +4,11 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { EnumToUnion } from '@metamask/snaps-utils'; import { STATE_ENCRYPTION_MAGIC_VALUE } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray, Hex } from '@metamask/utils'; import { isObject, getJsonSize, assert, isValidJson } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; import { deriveEntropy } from '../utils'; @@ -225,7 +225,7 @@ async function decryptState({ return decryptedState as Record; } catch { - throw ethErrors.rpc.internal({ + throw rpcErrors.internal({ message: 'Failed to decrypt snap state, the state must be corrupted.', }); } @@ -307,7 +307,7 @@ export function getManageStateImplementation({ } default: - throw ethErrors.rpc.invalidParams( + throw rpcErrors.invalidParams( `Invalid ${method} operation: "${operation as string}"`, ); } @@ -329,7 +329,7 @@ export function getValidatedParams( storageSizeLimit = STORAGE_SIZE_LIMIT, ): ManageStateArgs { if (!isObject(params)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected params to be a single object.', }); } @@ -341,14 +341,14 @@ export function getValidatedParams( typeof operation !== 'string' || !(Object.values(ManageStateOperation) as string[]).includes(operation) ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Must specify a valid manage state "operation".', }); } if (operation === ManageStateOperation.UpdateState) { if (!isObject(newState)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Invalid ${method} "updateState" parameter: The new state must be a plain object.`, data: { receivedNewState: @@ -362,7 +362,7 @@ export function getValidatedParams( // `getJsonSize` will throw if the state is not JSON serializable. size = getJsonSize(newState); } catch { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Invalid ${method} "updateState" parameter: The new state must be JSON serializable.`, data: { receivedNewState: @@ -372,7 +372,7 @@ export function getValidatedParams( } if (size > storageSizeLimit) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: `Invalid ${method} "updateState" parameter: The new state must not exceed ${storageSizeLimit} bytes in size.`, data: { receivedNewState: diff --git a/packages/snaps-rpc-methods/src/restricted/notify.ts b/packages/snaps-rpc-methods/src/restricted/notify.ts index 636d077ec5..c7b594d191 100644 --- a/packages/snaps-rpc-methods/src/restricted/notify.ts +++ b/packages/snaps-rpc-methods/src/restricted/notify.ts @@ -4,10 +4,10 @@ import type { ValidPermissionSpecification, } from '@metamask/permission-controller'; import { PermissionType, SubjectType } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; import type { EnumToUnion } from '@metamask/snaps-utils'; import type { NonEmptyArray } from '@metamask/utils'; import { isObject } from '@metamask/utils'; -import { ethErrors } from 'eth-rpc-errors'; import type { MethodHooksObject } from '../utils'; @@ -127,7 +127,7 @@ export function getImplementation({ case NotificationType.InApp: return await showInAppNotification(origin, validatedParams); default: - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Must specify a valid notification "type".', }); } @@ -143,7 +143,7 @@ export function getImplementation({ */ export function getValidatedParams(params: unknown): NotificationArgs { if (!isObject(params)) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Expected params to be a single object.', }); } @@ -155,14 +155,14 @@ export function getValidatedParams(params: unknown): NotificationArgs { typeof type !== 'string' || !Object.values(NotificationType).includes(type as NotificationType) ) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Must specify a valid notification "type".', }); } // Set to the max message length on a Mac notification for now. if (!message || typeof message !== 'string' || message.length >= 50) { - throw ethErrors.rpc.invalidParams({ + throw rpcErrors.invalidParams({ message: 'Must specify a non-empty string "message" less than 50 characters long.', }); diff --git a/packages/snaps-simulator/package.json b/packages/snaps-simulator/package.json index 93c574b91c..ed88ed2cdf 100644 --- a/packages/snaps-simulator/package.json +++ b/packages/snaps-simulator/package.json @@ -53,10 +53,11 @@ "@ethersproject/units": "^5.7.0", "@metamask/base-controller": "^3.2.0", "@metamask/browser-passworder": "^4.1.0", - "@metamask/eth-json-rpc-middleware": "^11.0.2", + "@metamask/eth-json-rpc-middleware": "^12.0.0", + "@metamask/json-rpc-engine": "^7.1.1", "@metamask/key-tree": "^9.0.0", - "@metamask/permission-controller": "^4.1.2", - "@metamask/rpc-errors": "^5.1.1", + "@metamask/permission-controller": "^5.0.0", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-controllers": "workspace:^", "@metamask/snaps-execution-environments": "workspace:^", "@metamask/snaps-rpc-methods": "workspace:^", @@ -68,8 +69,7 @@ "date-fns": "^2.30.0", "fast-deep-equal": "^3.1.3", "framer-motion": "^10.12.8", - "json-rpc-engine": "^6.1.0", - "json-rpc-middleware-stream": "^4.2.0", + "json-rpc-middleware-stream": "^5.0.0", "monaco-editor": "^0.38.0", "react": "^18.2.0", "react-dnd": "^16.0.1", diff --git a/packages/snaps-simulator/src/features/simulation/middleware.test.ts b/packages/snaps-simulator/src/features/simulation/middleware.test.ts index 3e3e1078ab..4f7230ccd7 100644 --- a/packages/snaps-simulator/src/features/simulation/middleware.test.ts +++ b/packages/snaps-simulator/src/features/simulation/middleware.test.ts @@ -1,5 +1,5 @@ +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { mnemonicPhraseToBytes } from '@metamask/key-tree'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { DEFAULT_SRP } from '../configuration'; import { createMiscMethodMiddleware } from './middleware'; diff --git a/packages/snaps-simulator/src/features/simulation/middleware.ts b/packages/snaps-simulator/src/features/simulation/middleware.ts index 0b6ff69efa..df5641486c 100644 --- a/packages/snaps-simulator/src/features/simulation/middleware.ts +++ b/packages/snaps-simulator/src/features/simulation/middleware.ts @@ -1,12 +1,16 @@ -import { BIP44Node } from '@metamask/key-tree'; -import { logError } from '@metamask/snaps-utils'; import type { JsonRpcEngineEndCallback, JsonRpcEngineNextCallback, JsonRpcMiddleware, +} from '@metamask/json-rpc-engine'; +import { BIP44Node } from '@metamask/key-tree'; +import { logError } from '@metamask/snaps-utils'; +import type { + Json, + JsonRpcParams, JsonRpcRequest, PendingJsonRpcResponse, -} from 'json-rpc-engine'; +} from '@metamask/utils'; /* eslint-disable @typescript-eslint/naming-convention */ export const methodHandlers = { @@ -30,8 +34,8 @@ export type MiscMiddlewareHooks = { * @param hooks - Any hooks required by this handler. */ async function getAccountsHandler( - _request: JsonRpcRequest, - response: PendingJsonRpcResponse, + _request: JsonRpcRequest, + response: PendingJsonRpcResponse, _next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, hooks: MiscMiddlewareHooks, @@ -62,8 +66,8 @@ async function getAccountsHandler( * @param end - The json-rpc-engine middleware end handler. */ async function getProviderStateHandler( - _request: JsonRpcRequest, - response: PendingJsonRpcResponse, + _request: JsonRpcRequest, + response: PendingJsonRpcResponse, _next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, ) { @@ -87,7 +91,7 @@ async function getProviderStateHandler( */ export function createMiscMethodMiddleware( hooks: MiscMiddlewareHooks, -): JsonRpcMiddleware { +): JsonRpcMiddleware { // This should probably use createAsyncMiddleware // eslint-disable-next-line @typescript-eslint/no-misused-promises return async function methodMiddleware(request, response, next, end) { diff --git a/packages/snaps-simulator/src/features/simulation/sagas.ts b/packages/snaps-simulator/src/features/simulation/sagas.ts index 405aac5e54..f99206a0ee 100644 --- a/packages/snaps-simulator/src/features/simulation/sagas.ts +++ b/packages/snaps-simulator/src/features/simulation/sagas.ts @@ -1,6 +1,7 @@ import { ControllerMessenger } from '@metamask/base-controller'; import { encrypt, decrypt } from '@metamask/browser-passworder'; import { createFetchMiddleware } from '@metamask/eth-json-rpc-middleware'; +import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import { mnemonicPhraseToBytes } from '@metamask/key-tree'; import type { GenericPermissionController } from '@metamask/permission-controller'; import { @@ -30,7 +31,6 @@ import type { import { logError } from '@metamask/snaps-utils'; import { getSafeJson } from '@metamask/utils'; import type { PayloadAction } from '@reduxjs/toolkit'; -import { JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import type { SagaIterator } from 'redux-saga'; import { all, call, put, select, takeLatest } from 'redux-saga/effects'; diff --git a/packages/snaps-types/package.json b/packages/snaps-types/package.json index 9cd444b6f8..d0024f02a4 100644 --- a/packages/snaps-types/package.json +++ b/packages/snaps-types/package.json @@ -35,7 +35,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/providers": "^11.1.1", + "@metamask/providers": "^13.0.0", "@metamask/snaps-rpc-methods": "workspace:^", "@metamask/snaps-utils": "workspace:^", "@metamask/utils": "^8.1.0" diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index 8dbdac37b1..1bed4d70a9 100644 --- a/packages/snaps-utils/package.json +++ b/packages/snaps-utils/package.json @@ -69,7 +69,8 @@ "@babel/types": "^7.23.0", "@metamask/base-controller": "^3.2.0", "@metamask/key-tree": "^9.0.0", - "@metamask/permission-controller": "^4.1.2", + "@metamask/permission-controller": "^5.0.0", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-registry": "^2.1.0", "@metamask/snaps-ui": "workspace:^", "@metamask/utils": "^8.1.0", @@ -77,7 +78,6 @@ "@scure/base": "^1.1.1", "chalk": "^4.1.2", "cron-parser": "^4.5.0", - "eth-rpc-errors": "^4.0.3", "fast-deep-equal": "^3.1.3", "fast-json-stable-stringify": "^2.1.0", "is-svg": "^4.4.0", diff --git a/packages/snaps-utils/src/errors.test.ts b/packages/snaps-utils/src/errors.test.ts index 11954f7581..930821bca9 100644 --- a/packages/snaps-utils/src/errors.test.ts +++ b/packages/snaps-utils/src/errors.test.ts @@ -1,4 +1,4 @@ -import { ethErrors } from 'eth-rpc-errors'; +import { rpcErrors } from '@metamask/rpc-errors'; import { getErrorMessage } from './errors'; @@ -6,7 +6,7 @@ describe('getErrorMessage', () => { it('returns the error message if the error is an object with a message property', () => { expect(getErrorMessage(new Error('foo'))).toBe('foo'); expect(getErrorMessage({ message: 'foo' })).toBe('foo'); - expect(getErrorMessage(ethErrors.rpc.invalidParams('foo'))).toBe('foo'); + expect(getErrorMessage(rpcErrors.invalidParams('foo'))).toBe('foo'); }); it('returns the error converted to a string if the error does not have a message property', () => { diff --git a/packages/snaps-utils/src/snaps.ts b/packages/snaps-utils/src/snaps.ts index 481f276eb5..e59b1f74b1 100644 --- a/packages/snaps-utils/src/snaps.ts +++ b/packages/snaps-utils/src/snaps.ts @@ -4,10 +4,14 @@ import type { PermissionConstraint, } from '@metamask/permission-controller'; import type { BlockReason } from '@metamask/snaps-registry'; -import type { Json, SemVerVersion, Opaque } from '@metamask/utils'; +import type { + Json, + JsonRpcError, + Opaque, + SemVerVersion, +} from '@metamask/utils'; import { assert, isObject, assertStruct } from '@metamask/utils'; import { base64 } from '@scure/base'; -import type { SerializedEthereumRpcError } from 'eth-rpc-errors/dist/classes'; import stableStringify from 'fast-json-stable-stringify'; import type { Struct } from 'superstruct'; import { @@ -164,9 +168,7 @@ export type TruncatedSnapFields = */ export type TruncatedSnap = Pick; -export type ProcessSnapResult = - | TruncatedSnap - | { error: SerializedEthereumRpcError }; +export type ProcessSnapResult = TruncatedSnap | { error: JsonRpcError }; export type InstallSnapsResult = Record; diff --git a/packages/test-snaps/package.json b/packages/test-snaps/package.json index fff2a417dd..491201ee0a 100644 --- a/packages/test-snaps/package.json +++ b/packages/test-snaps/package.json @@ -64,7 +64,7 @@ "@metamask/eslint-config-jest": "^12.1.0", "@metamask/eslint-config-nodejs": "^12.1.0", "@metamask/eslint-config-typescript": "^12.1.0", - "@metamask/providers": "^11.1.1", + "@metamask/providers": "^13.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", diff --git a/yarn.lock b/yarn.lock index 3770b722ff..92a90d92a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2856,7 +2856,7 @@ __metadata: languageName: node linkType: hard -"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": +"@esbuild-plugins/node-modules-polyfill@npm:0.2.2": version: 0.2.2 resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" dependencies: @@ -2868,6 +2868,18 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-modules-polyfill@patch:@esbuild-plugins/node-modules-polyfill@npm%3A0.2.2#./.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch::locator=root%40workspace%3A.": + version: 0.2.2 + resolution: "@esbuild-plugins/node-modules-polyfill@patch:@esbuild-plugins/node-modules-polyfill@npm%3A0.2.2#./.yarn/patches/@esbuild-plugins-node-modules-polyfill-npm-0.2.2-f612681798.patch::version=0.2.2&hash=2fbb1f&locator=root%40workspace%3A." + dependencies: + escape-string-regexp: ^4.0.0 + rollup-plugin-node-polyfills: ^0.2.1 + peerDependencies: + esbuild: "*" + checksum: f74c0754f737126d437c9f850c0a89f17df6ef3a0962d38ba518e48a7510096d6c97017417dbf2b49bd1b8df350af8a29bca323efaf3be670d08c820baae8cf1 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -3682,13 +3694,13 @@ __metadata: languageName: node linkType: hard -"@metamask/abi-utils@npm:^1.2.0": - version: 1.2.0 - resolution: "@metamask/abi-utils@npm:1.2.0" +"@metamask/abi-utils@npm:^2.0.2": + version: 2.0.2 + resolution: "@metamask/abi-utils@npm:2.0.2" dependencies: - "@metamask/utils": ^3.4.1 + "@metamask/utils": ^8.0.0 superstruct: ^1.0.3 - checksum: 55fde5bcbc7b2b72fb469867e3f2c41fddb1b2e992c6ea846de5701ad8fa5fcc66701facf1df793f8f58b8befcaa3c21a5e5519e839cc6fd5a3932806db7a5d5 + checksum: 5ec153e7691a4e1dc8738a0ba1a99a354ddb13851fa88a40a19f002f6308310e71c2cee28c3a25d9f7f67e839c7dffe4760e93e308dd17fa725b08d0dc73a3d4 languageName: node linkType: hard @@ -3703,16 +3715,16 @@ __metadata: languageName: node linkType: hard -"@metamask/approval-controller@npm:^3.5.0, @metamask/approval-controller@npm:^3.5.2": - version: 3.5.2 - resolution: "@metamask/approval-controller@npm:3.5.2" +"@metamask/approval-controller@npm:^4.0.0": + version: 4.0.0 + resolution: "@metamask/approval-controller@npm:4.0.0" dependencies: - "@metamask/base-controller": ^3.2.2 - "@metamask/utils": ^6.2.0 - eth-rpc-errors: ^4.0.2 + "@metamask/base-controller": ^3.2.3 + "@metamask/rpc-errors": ^6.0.0 + "@metamask/utils": ^8.1.0 immer: ^9.0.6 nanoid: ^3.1.31 - checksum: 70436be566952b8aa42de48aff36655a42611478a5beee4e5ea9cf019ef386db202c464d73057df4e9cce22ebdb7517f5eb71a9def6bd7672f48a2d714ad1f2c + checksum: cc21b5644a20fe3b93882c71d0c1134fbfb9f60ec7de0bbf53ffe5fc1207744cb83cdfc0c661a2252956e0d06ef27cdf25ac80bcca36a6fa4eae15372ae30972 languageName: node linkType: hard @@ -3731,13 +3743,13 @@ __metadata: languageName: node linkType: hard -"@metamask/base-controller@npm:^3.2.0, @metamask/base-controller@npm:^3.2.2": - version: 3.2.2 - resolution: "@metamask/base-controller@npm:3.2.2" +"@metamask/base-controller@npm:^3.2.0, @metamask/base-controller@npm:^3.2.3": + version: 3.2.3 + resolution: "@metamask/base-controller@npm:3.2.3" dependencies: - "@metamask/utils": ^6.2.0 + "@metamask/utils": ^8.1.0 immer: ^9.0.6 - checksum: 90e639b4415bd9e0f8c86b5fdfa9e164c2615bc69f85c5028dc3657883aa20b66d0ecb00850e703d0f2495c3deda2e17bb18e5f2a4b24aca7943b61b67bff82e + checksum: f49fcf2bf892ec25657c2d72a50b3c4f3cad59acb1b74d9fdcdf564107b8f38f73647c696aaa9699d94828b5797d8f1479dab44a2dbcda987c268b0088bb3b76 languageName: node linkType: hard @@ -3753,7 +3765,7 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/key-tree": ^9.0.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -3795,7 +3807,7 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/key-tree": ^9.0.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -3843,7 +3855,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -3881,7 +3893,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-browserify-plugin": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -3921,7 +3933,7 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/key-tree": ^9.0.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -3949,19 +3961,18 @@ __metadata: languageName: unknown linkType: soft -"@metamask/controller-utils@npm:^5.0.1": - version: 5.0.1 - resolution: "@metamask/controller-utils@npm:5.0.1" +"@metamask/controller-utils@npm:^5.0.2": + version: 5.0.2 + resolution: "@metamask/controller-utils@npm:5.0.2" dependencies: "@metamask/eth-query": ^3.0.1 - "@metamask/utils": ^6.2.0 + "@metamask/utils": ^8.1.0 "@spruceid/siwe-parser": 1.1.3 eth-ens-namehash: ^2.0.8 - eth-rpc-errors: ^4.0.2 ethereumjs-util: ^7.0.10 ethjs-unit: ^0.1.6 fast-deep-equal: ^3.1.3 - checksum: bf1566448674a9443a9f40ad46aa152164256dfbfbf3aacf80ef7b2df99e790f17f655ee2fede2a52ff9626828c99cc32121637661d8f496a844a48d8237cc08 + checksum: 2345ab9ee0ba900fe2249d80009acfcf458bc60b30418234d00f5f04247b1182a585050572237f8ab09aa23032a24b99ad96399fc0798a0e9a114a29c3bf90d6 languageName: node linkType: hard @@ -3977,7 +3988,7 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/key-tree": ^9.0.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4084,7 +4095,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4123,7 +4134,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4247,30 +4258,31 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-json-rpc-middleware@npm:^11.0.2": - version: 11.0.2 - resolution: "@metamask/eth-json-rpc-middleware@npm:11.0.2" +"@metamask/eth-json-rpc-middleware@npm:^12.0.0": + version: 12.0.0 + resolution: "@metamask/eth-json-rpc-middleware@npm:12.0.0" dependencies: - "@metamask/eth-json-rpc-provider": ^1.0.0 - "@metamask/eth-sig-util": ^6.0.0 - "@metamask/utils": ^5.0.1 - clone: ^2.1.1 - eth-block-tracker: ^7.0.1 - eth-rpc-errors: ^4.0.3 - json-rpc-engine: ^6.1.0 - pify: ^3.0.0 - safe-stable-stringify: ^2.3.2 - checksum: e548012b65d33111618e4a30a21b82f22d473e6f9d1ed98f5a8b7db61ffad956f2a09a0196f60bd0ac800f4ed1b19ddb16f680915112a6649fcc2084412ecd0f + "@metamask/eth-json-rpc-provider": ^2.1.0 + "@metamask/eth-sig-util": ^7.0.0 + "@metamask/json-rpc-engine": ^7.1.1 + "@metamask/rpc-errors": ^6.0.0 + "@metamask/utils": ^8.1.0 + eth-block-tracker: ^8.0.0 + klona: ^2.0.6 + pify: ^5.0.0 + safe-stable-stringify: ^2.4.3 + checksum: 22391116f752abcb0145385297b426450260e857d9a37b6aeb7602f48079126e0920d39d28ec3e6de8fc247d589dd089731198af4f92aa98974fa45d37ede027 languageName: node linkType: hard -"@metamask/eth-json-rpc-provider@npm:^1.0.0": - version: 1.0.0 - resolution: "@metamask/eth-json-rpc-provider@npm:1.0.0" +"@metamask/eth-json-rpc-provider@npm:^2.1.0": + version: 2.2.0 + resolution: "@metamask/eth-json-rpc-provider@npm:2.2.0" dependencies: - "@metamask/safe-event-emitter": ^2.0.0 - json-rpc-engine: ^6.1.0 - checksum: 27865d84d90030db1a9e5a66bc0b0ae079706fb7be635ec1e9bd4f64771e819aae78f0a026c6629d3a1a2eb277fcd51977315c049c47a70df1dd95d1d4106982 + "@metamask/json-rpc-engine": ^7.1.0 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + checksum: da725fa51e8bfe0b904520b8223aed209fc54605edf1ab5ae6091a460694fd4aad5046f3ae88e8df3741079507dc0e6f2e2c85f1feee8a98506c4f550ea07549 languageName: node linkType: hard @@ -4284,18 +4296,18 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^6.0.0": - version: 6.0.1 - resolution: "@metamask/eth-sig-util@npm:6.0.1" +"@metamask/eth-sig-util@npm:^7.0.0": + version: 7.0.0 + resolution: "@metamask/eth-sig-util@npm:7.0.0" dependencies: "@ethereumjs/util": ^8.1.0 - "@metamask/abi-utils": ^1.2.0 - "@metamask/utils": ^5.0.2 + "@metamask/abi-utils": ^2.0.2 + "@metamask/utils": ^8.1.0 ethereum-cryptography: ^2.1.2 ethjs-util: ^0.1.6 tweetnacl: ^1.0.3 tweetnacl-util: ^0.15.1 - checksum: 6a9e64991bf826b882c0e42499052c5b51f7a2d5db77ac65ac64be1d14dd498569a4cade3cbad6213b6a9f7e508eaff619eda9c9ea448377e94e16773b755a5c + checksum: bcb6bd23333e0b4dcb49f8772483dcb4c27e75405a2b111f1eafe0b341b221cf86ba4843e91c567d8836e80b6049d8e2f89c6766c62bbd256533e0f256f6d846 languageName: node linkType: hard @@ -4310,7 +4322,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4348,7 +4360,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4417,7 +4429,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4494,7 +4506,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4588,6 +4600,17 @@ __metadata: languageName: unknown linkType: soft +"@metamask/json-rpc-engine@npm:^7.1.0, @metamask/json-rpc-engine@npm:^7.1.1": + version: 7.1.1 + resolution: "@metamask/json-rpc-engine@npm:7.1.1" + dependencies: + "@metamask/rpc-errors": ^6.0.0 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + checksum: 9dddd9142965ccd86313cda5bf13f15bf99c6c14631f93aab78de353317d548a334b5b125cdc134edd7d54e2f2e4961a0bdcd24fba997b2913083955df8fefa1 + languageName: node + linkType: hard + "@metamask/json-rpc-example-snap@workspace:^, @metamask/json-rpc-example-snap@workspace:packages/examples/packages/json-rpc": version: 0.0.0-use.local resolution: "@metamask/json-rpc-example-snap@workspace:packages/examples/packages/json-rpc" @@ -4599,7 +4622,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4687,7 +4710,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4760,7 +4783,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4798,7 +4821,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4835,23 +4858,23 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-controller@npm:^4.1.2": - version: 4.1.2 - resolution: "@metamask/permission-controller@npm:4.1.2" +"@metamask/permission-controller@npm:^5.0.0": + version: 5.0.0 + resolution: "@metamask/permission-controller@npm:5.0.0" dependencies: - "@metamask/approval-controller": ^3.5.2 - "@metamask/base-controller": ^3.2.2 - "@metamask/controller-utils": ^5.0.1 - "@metamask/utils": ^6.2.0 + "@metamask/approval-controller": ^4.0.0 + "@metamask/base-controller": ^3.2.3 + "@metamask/controller-utils": ^5.0.2 + "@metamask/json-rpc-engine": ^7.1.1 + "@metamask/rpc-errors": ^6.0.0 + "@metamask/utils": ^8.1.0 "@types/deep-freeze-strict": ^1.1.0 deep-freeze-strict: ^1.1.1 - eth-rpc-errors: ^4.0.2 immer: ^9.0.6 - json-rpc-engine: ^6.1.0 nanoid: ^3.1.31 peerDependencies: - "@metamask/approval-controller": ^3.5.2 - checksum: 743536cc127b4f8ee85c23c79f92e9fa635d4ce5a3e01f7e24e519e507dd1461282b854d97e147312b15e94f08309cd8144b03174dc793f725b85a1db2c9eb2a + "@metamask/approval-controller": ^4.0.0 + checksum: f79aeb5d8a22761ecfd1e8bee8f1fc3e4d4d9c0f8d823844f799a65657fa063d4b7df248efe0b585685ea27f95a72e4f906a40c228cd95d26ae2bc012c0713cf languageName: node linkType: hard @@ -4865,22 +4888,22 @@ __metadata: languageName: node linkType: hard -"@metamask/providers@npm:^11.1.1": - version: 11.1.1 - resolution: "@metamask/providers@npm:11.1.1" +"@metamask/providers@npm:^13.0.0": + version: 13.0.0 + resolution: "@metamask/providers@npm:13.0.0" dependencies: + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/object-multiplex": ^1.1.0 + "@metamask/rpc-errors": ^6.0.0 "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 detect-browser: ^5.2.0 - eth-rpc-errors: ^4.0.2 - extension-port-stream: ^2.0.1 + extension-port-stream: ^2.1.1 fast-deep-equal: ^3.1.3 is-stream: ^2.0.0 - json-rpc-engine: ^6.1.0 json-rpc-middleware-stream: ^4.2.1 - pump: ^3.0.0 webextension-polyfill: ^0.10.0 - checksum: ca0339e5219ef43bccdf1debbf610f5f06c95d879c7bba124463c4c6e6dd34fae30c0cdf673985d85d9326a87e94a6ee7ad5d29ef154dca85a0c672fdab152d1 + checksum: b41748cf179794bf7a68d3028e84234e876498611221ba40846ed0859b4470a806c4cf99ff9fcdc60fde1af2f0d999281e6edbdd42113223d8f7018a009ff0ab languageName: node linkType: hard @@ -4898,7 +4921,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-rollup-plugin": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4939,10 +4962,13 @@ __metadata: languageName: node linkType: hard -"@metamask/safe-event-emitter@npm:^2.0.0": - version: 2.0.0 - resolution: "@metamask/safe-event-emitter@npm:2.0.0" - checksum: 8b717ac5d53df0027c05509f03d0534700b5898dd1c3a53fb2dc4c0499ca5971b14aae67f522d09eb9f509e77f50afa95fdb3eda1afbff8b071c18a3d2905e93 +"@metamask/rpc-errors@npm:^6.0.0, @metamask/rpc-errors@npm:^6.1.0": + version: 6.1.0 + resolution: "@metamask/rpc-errors@npm:6.1.0" + dependencies: + "@metamask/utils": ^8.1.0 + fast-safe-stringify: ^2.0.6 + checksum: 9f4821d804e2fcaa8987b0958d02c6d829b7c7db49740c811cb593f381d0c4b00dabb7f1802907f1b2f6126f7c0d83ec34219183d29650f5d24df014ac72906a languageName: node linkType: hard @@ -5106,16 +5132,19 @@ __metadata: "@esbuild-plugins/node-globals-polyfill": ^0.2.3 "@esbuild-plugins/node-modules-polyfill": ^0.2.2 "@lavamoat/allow-scripts": ^2.5.1 - "@metamask/approval-controller": ^3.5.0 + "@metamask/approval-controller": ^4.0.0 "@metamask/auto-changelog": ^3.3.0 "@metamask/base-controller": ^3.2.0 "@metamask/eslint-config": ^12.1.0 "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/object-multiplex": ^1.2.0 - "@metamask/permission-controller": ^4.1.2 + "@metamask/permission-controller": ^5.0.0 "@metamask/post-message-stream": ^7.0.0 + "@metamask/rpc-errors": ^6.1.0 + "@metamask/snaps-execution-environments": "workspace:^" "@metamask/snaps-registry": ^2.1.0 "@metamask/snaps-rpc-methods": "workspace:^" "@metamask/snaps-utils": "workspace:^" @@ -5153,7 +5182,6 @@ __metadata: eslint-plugin-n: ^15.7.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.1.1 - eth-rpc-errors: ^4.0.3 expect-webdriverio: ^4.4.1 gunzip-maybe: ^1.4.2 immer: ^9.0.6 @@ -5162,8 +5190,7 @@ __metadata: istanbul-reports: ^3.1.5 jest: ^29.0.2 jest-fetch-mock: ^3.0.3 - json-rpc-engine: ^6.1.0 - json-rpc-middleware-stream: ^4.2.0 + json-rpc-middleware-stream: ^5.0.0 mkdirp: ^1.0.4 nanoid: ^3.1.31 prettier: ^2.7.1 @@ -5203,9 +5230,11 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/object-multiplex": ^1.2.0 "@metamask/post-message-stream": ^7.0.0 - "@metamask/providers": ^11.1.1 + "@metamask/providers": ^13.0.0 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-rpc-methods": "workspace:^" "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.1.0 @@ -5237,7 +5266,6 @@ __metadata: eslint-plugin-n: ^15.7.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.1.1 - eth-rpc-errors: ^4.0.3 expect-webdriverio: ^4.4.1 istanbul-lib-coverage: ^3.2.0 istanbul-lib-report: ^3.0.0 @@ -5245,7 +5273,6 @@ __metadata: jest: ^29.0.2 jest-environment-node: ^29.5.0 jest-fetch-mock: ^3.0.3 - json-rpc-engine: ^6.1.0 lavamoat: ^7.3.1 lavamoat-browserify: ^15.9.1 nanoid: ^3.1.31 @@ -5380,11 +5407,12 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.1.2 + "@metamask/permission-controller": ^5.0.0 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-ui": "workspace:^" "@metamask/snaps-utils": "workspace:^" - "@metamask/types": ^1.1.0 "@metamask/utils": ^8.1.0 "@noble/hashes": ^1.3.1 "@swc/cli": ^0.1.62 @@ -5403,11 +5431,9 @@ __metadata: eslint-plugin-n: ^15.7.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.1.1 - eth-rpc-errors: ^4.0.3 expect-type: ^0.15.0 jest: ^29.0.2 jest-it-up: ^2.0.0 - json-rpc-engine: ^6.1.0 prettier: ^2.7.1 prettier-plugin-packagejson: ^2.2.11 rimraf: ^4.1.2 @@ -5433,10 +5459,11 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/eth-json-rpc-middleware": ^11.0.2 + "@metamask/eth-json-rpc-middleware": ^12.0.0 + "@metamask/json-rpc-engine": ^7.1.1 "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.1.2 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/permission-controller": ^5.0.0 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-controllers": "workspace:^" "@metamask/snaps-execution-environments": "workspace:^" "@metamask/snaps-rpc-methods": "workspace:^" @@ -5486,8 +5513,7 @@ __metadata: jest-environment-jsdom: ^29.5.0 jest-fetch-mock: ^3.0.3 jest-it-up: ^2.0.0 - json-rpc-engine: ^6.1.0 - json-rpc-middleware-stream: ^4.2.0 + json-rpc-middleware-stream: ^5.0.0 monaco-editor: ^0.38.0 monaco-editor-webpack-plugin: ^7.0.1 prettier: ^2.7.1 @@ -5531,7 +5557,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/providers": ^11.1.1 + "@metamask/providers": ^13.0.0 "@metamask/snaps-rpc-methods": "workspace:^" "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.1.0 @@ -5610,8 +5636,9 @@ __metadata: "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 "@metamask/key-tree": ^9.0.0 - "@metamask/permission-controller": ^4.1.2 + "@metamask/permission-controller": ^5.0.0 "@metamask/post-message-stream": ^7.0.0 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-registry": ^2.1.0 "@metamask/snaps-ui": "workspace:^" "@metamask/utils": ^8.1.0 @@ -5647,7 +5674,6 @@ __metadata: eslint-plugin-n: ^15.7.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.1.1 - eth-rpc-errors: ^4.0.3 expect-webdriverio: ^4.4.1 fast-deep-equal: ^3.1.3 fast-json-stable-stringify: ^2.1.0 @@ -5748,7 +5774,7 @@ __metadata: "@metamask/name-lookup-example-snap": "workspace:^" "@metamask/network-example-snap": "workspace:^" "@metamask/notification-example-snap": "workspace:^" - "@metamask/providers": ^11.1.1 + "@metamask/providers": ^13.0.0 "@metamask/snaps-utils": "workspace:^" "@metamask/utils": ^8.1.0 "@metamask/wasm-example-snap": "workspace:^" @@ -5801,26 +5827,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/types@npm:^1.1.0": - version: 1.1.0 - resolution: "@metamask/types@npm:1.1.0" - checksum: 500e8c076a2b0a6d688c8c465101256f090547d99c9a5585efe3d1db7a494b6b2712b127043fb316912caffc4fff78976b9a7780780abb68305e8a3bf812689c - languageName: node - linkType: hard - -"@metamask/utils@npm:^3.4.1": - version: 3.6.0 - resolution: "@metamask/utils@npm:3.6.0" - dependencies: - "@types/debug": ^4.1.7 - debug: ^4.3.4 - semver: ^7.3.8 - superstruct: ^1.0.3 - checksum: 1ebc6677bb017e4d09d4af143621fe27194d8ed815234cfd76469c3c734dc1db2ea7b577c01a2096c21c04d8c9c4d721d3035b5353fe2ded3b4737f326755e43 - languageName: node - linkType: hard - -"@metamask/utils@npm:^5.0.0, @metamask/utils@npm:^5.0.1, @metamask/utils@npm:^5.0.2": +"@metamask/utils@npm:^5.0.0, @metamask/utils@npm:^5.0.2": version: 5.0.2 resolution: "@metamask/utils@npm:5.0.2" dependencies: @@ -5833,7 +5840,7 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^6.0.1, @metamask/utils@npm:^6.2.0": +"@metamask/utils@npm:^6.0.1": version: 6.2.0 resolution: "@metamask/utils@npm:6.2.0" dependencies: @@ -5847,7 +5854,7 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^8.1.0": +"@metamask/utils@npm:^8.0.0, @metamask/utils@npm:^8.1.0": version: 8.1.0 resolution: "@metamask/utils@npm:8.1.0" dependencies: @@ -5872,7 +5879,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -5910,7 +5917,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" "@metamask/snaps-webpack-plugin": "workspace:^" @@ -10182,13 +10189,6 @@ __metadata: languageName: node linkType: hard -"clone@npm:^2.1.1": - version: 2.1.2 - resolution: "clone@npm:2.1.2" - checksum: aaf106e9bc025b21333e2f4c12da539b568db4925c0501a1bf4070836c9e848c892fa22c35548ce0d1132b08bbbfa17a00144fe58fccdab6fa900fec4250f67d - languageName: node - linkType: hard - "cmd-shim@npm:^6.0.0": version: 6.0.1 resolution: "cmd-shim@npm:6.0.1" @@ -12549,16 +12549,16 @@ __metadata: languageName: node linkType: hard -"eth-block-tracker@npm:^7.0.1": - version: 7.1.0 - resolution: "eth-block-tracker@npm:7.1.0" +"eth-block-tracker@npm:^8.0.0": + version: 8.0.0 + resolution: "eth-block-tracker@npm:8.0.0" dependencies: - "@metamask/eth-json-rpc-provider": ^1.0.0 + "@metamask/eth-json-rpc-provider": ^2.1.0 "@metamask/safe-event-emitter": ^3.0.0 - "@metamask/utils": ^5.0.1 + "@metamask/utils": ^8.1.0 json-rpc-random-id: ^1.0.1 - pify: ^3.0.0 - checksum: 1d019f261e0ef07387cd74538b160700caa35ba9859ab9d4e5137c48bf9c92822c3b4ade40f8a504f16cb813de4c317c5378d047625ddf04592e256be8842588 + pify: ^5.0.0 + checksum: 3416c2ee653f81d1f71f3a9b80e04837fb516494f64ded45c053dfc24c6c6ce8dac7e5b8376cd57f52838f43a93d20a8e17d4d875e50d1e4c267543ffe0e6ad8 languageName: node linkType: hard @@ -12572,15 +12572,6 @@ __metadata: languageName: node linkType: hard -"eth-rpc-errors@npm:^4.0.2, eth-rpc-errors@npm:^4.0.3": - version: 4.0.3 - resolution: "eth-rpc-errors@npm:4.0.3" - dependencies: - fast-safe-stringify: ^2.0.6 - checksum: 5fa31d1a10fdb340733b9a55e38e7687222c501052ca20743cef4d0c911a9bbcc0cad54aa6bf3e4b428604c071ff519803060e1cbc79ddb7c9257c11d407d32a - languageName: node - linkType: hard - "ethereum-cryptography@npm:^0.1.3": version: 0.1.3 resolution: "ethereum-cryptography@npm:0.1.3" @@ -12887,12 +12878,12 @@ __metadata: languageName: node linkType: hard -"extension-port-stream@npm:^2.0.1": - version: 2.0.1 - resolution: "extension-port-stream@npm:2.0.1" +"extension-port-stream@npm:^2.1.1": + version: 2.1.1 + resolution: "extension-port-stream@npm:2.1.1" dependencies: - webextension-polyfill-ts: ^0.22.0 - checksum: e127fd94a9b7b2b847d5f292fa940b6f63d1088ea5ed6a1e3142628b358a503881f1a04e2d8ad5aec2642f7672e054e16accd933bf9cdcfa75465aba32470d07 + webextension-polyfill: ">=0.10.0 <1.0" + checksum: aee8bbeb2ed6f69a62f58a89580e0e9002dadb11062edbaedb7bb04cfc5a5e0b0d3980bfeaa1c3ee7e08dec7e5fac26e25497fc2f82000db7653442bd5eca157 languageName: node linkType: hard @@ -15992,23 +15983,25 @@ __metadata: languageName: node linkType: hard -"json-rpc-engine@npm:^6.1.0": - version: 6.1.0 - resolution: "json-rpc-engine@npm:6.1.0" +"json-rpc-middleware-stream@npm:^4.2.1": + version: 4.2.2 + resolution: "json-rpc-middleware-stream@npm:4.2.2" dependencies: - "@metamask/safe-event-emitter": ^2.0.0 - eth-rpc-errors: ^4.0.2 - checksum: 33b6c9bbd81abf8e323a0281ee05871713203c40d34a4d0bda27706cd0a0935c7b51845238ba89b73027e44ebc8034bbd82db9f962e6c578eb922d9b95acc8bd + "@metamask/safe-event-emitter": ^3.0.0 + readable-stream: ^2.3.3 + checksum: 01ff3a23b501fde5c2abb8c3b4d100c4fd430b41cf5e7750235f860a02d5823f8a43b0e81150c1d3bb196737f2273af1c7a50ff179e95e3d59fb7fe172249de3 languageName: node linkType: hard -"json-rpc-middleware-stream@npm:^4.2.0, json-rpc-middleware-stream@npm:^4.2.1": - version: 4.2.1 - resolution: "json-rpc-middleware-stream@npm:4.2.1" +"json-rpc-middleware-stream@npm:^5.0.0": + version: 5.0.1 + resolution: "json-rpc-middleware-stream@npm:5.0.1" dependencies: - "@metamask/safe-event-emitter": ^2.0.0 - readable-stream: ^2.3.3 - checksum: 207c34ba2c55ff072864422ba48b03f49dd1bc488f0d9c017c7474d3f2514bd4b1cc14daf5324f96887cdaf8e5c1018701960f55fb45e9c3224e3d7db9f70765 + "@metamask/json-rpc-engine": ^7.1.1 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.1.0 + readable-stream: ^3.6.2 + checksum: 1cfb8ef5fbb3daa15015213e380e79f043a4208d6ea5533a99b3f3c8aeb01270bfdce5b37003362745a059edbd418d9ca3548fab5fa83355641be2f392303084 languageName: node linkType: hard @@ -16177,6 +16170,13 @@ __metadata: languageName: node linkType: hard +"klona@npm:^2.0.6": + version: 2.0.6 + resolution: "klona@npm:2.0.6" + checksum: ac9ee3732e42b96feb67faae4d27cf49494e8a3bf3fa7115ce242fe04786788e0aff4741a07a45a2462e2079aa983d73d38519c85d65b70ef11447bbc3c58ce7 + languageName: node + linkType: hard + "ky@npm:^0.33.0": version: 0.33.2 resolution: "ky@npm:0.33.2" @@ -18467,13 +18467,6 @@ __metadata: languageName: node linkType: hard -"pify@npm:^3.0.0": - version: 3.0.0 - resolution: "pify@npm:3.0.0" - checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde - languageName: node - linkType: hard - "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -18481,6 +18474,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^5.0.0": + version: 5.0.0 + resolution: "pify@npm:5.0.0" + checksum: 443e3e198ad6bfa8c0c533764cf75c9d5bc976387a163792fb553ffe6ce923887cf14eebf5aea9b7caa8eab930da8c33612990ae85bd8c2bc18bedb9eae94ecb + languageName: node + linkType: hard + "pinkie-promise@npm:^2.0.0": version: 2.0.1 resolution: "pinkie-promise@npm:2.0.1" @@ -20197,7 +20197,7 @@ __metadata: languageName: node linkType: hard -"safe-stable-stringify@npm:^2.3.2": +"safe-stable-stringify@npm:^2.4.3": version: 2.4.3 resolution: "safe-stable-stringify@npm:2.4.3" checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 @@ -22739,29 +22739,13 @@ __metadata: languageName: node linkType: hard -"webextension-polyfill-ts@npm:^0.22.0": - version: 0.22.0 - resolution: "webextension-polyfill-ts@npm:0.22.0" - dependencies: - webextension-polyfill: ^0.7.0 - checksum: b7d60c787c2041458117f837914b6bc4f03c1685174ff7b751ad19192e232fa7e71a0ac7a22d73e898856a86de198e61e9cd59c63764279127c7ee973f3202d8 - languageName: node - linkType: hard - -"webextension-polyfill@npm:^0.10.0": +"webextension-polyfill@npm:>=0.10.0 <1.0, webextension-polyfill@npm:^0.10.0": version: 0.10.0 resolution: "webextension-polyfill@npm:0.10.0" checksum: 4a59036bda571360c2c0b2fb03fe1dc244f233946bcf9a6766f677956c40fd14d270aaa69cdba95e4ac521014afbe4008bfa5959d0ac39f91c990eb206587f91 languageName: node linkType: hard -"webextension-polyfill@npm:^0.7.0": - version: 0.7.0 - resolution: "webextension-polyfill@npm:0.7.0" - checksum: fb738a5de07feb593875e02f25c3ab4276c8736118929556c8d4bdf965bb0f11c96ea263cd397b9b21259e8faf2dce2eaaa42ce08c922d96de7adb5896ec7d10 - languageName: node - linkType: hard - "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" From 0f406148f1737e34ef65e1f257364c4d3e4e2ac7 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 20 Oct 2023 15:07:56 +0200 Subject: [PATCH 2/4] Implement `SnapError` (#1833) This implements the `SnapError` class, and wrapping/unwrapping of errors thrown from a Snap. `SnapError`s will not cause the Snap to crash, and can be used to respond to JSON-RPC requests for example. Closes #1824. --- .../packages/bip32/snap.manifest.json | 2 +- .../examples/packages/bip32/src/index.test.ts | 48 +- .../packages/bip44/snap.manifest.json | 2 +- .../examples/packages/bip44/src/index.test.ts | 35 +- .../browserify-plugin/src/index.test.ts | 11 +- .../packages/browserify/src/index.test.ts | 11 +- .../packages/dialogs/snap.manifest.json | 2 +- .../packages/dialogs/src/index.test.ts | 11 +- .../ethereum-provider/src/index.test.ts | 11 +- .../packages/ethers-js/src/index.test.ts | 22 +- .../packages/get-entropy/snap.manifest.json | 2 +- .../packages/get-entropy/src/index.test.ts | 11 +- .../examples/packages/get-file/package.json | 2 +- .../packages/get-file/snap.manifest.json | 2 +- .../packages/get-file/src/index.test.ts | 11 +- .../packages/get-locale/snap.manifest.json | 2 +- .../packages/get-locale/src/index.test.ts | 11 +- .../consumer-signer/src/index.test.ts | 11 +- .../packages/core-signer/snap.manifest.json | 2 +- .../packages/core-signer/src/index.test.ts | 11 +- .../packages/json-rpc/src/index.test.ts | 11 +- .../packages/manage-state/snap.manifest.json | 2 +- .../packages/manage-state/src/index.test.ts | 11 +- .../packages/name-lookup/src/index.test.ts | 2 +- .../packages/network-access/src/index.test.ts | 11 +- .../packages/notifications/snap.manifest.json | 2 +- .../packages/notifications/src/index.test.ts | 11 +- .../packages/rollup-plugin/src/index.test.ts | 11 +- .../examples/packages/wasm/snap.manifest.json | 2 +- .../examples/packages/wasm/src/index.test.ts | 11 +- packages/examples/packages/wasm/src/index.ts | 2 +- .../packages/webpack-plugin/src/index.test.ts | 11 +- packages/snaps-controllers/coverage.json | 6 +- packages/snaps-controllers/package.json | 12 - .../src/services/AbstractExecutionService.ts | 27 +- .../src/services/browser.test.ts | 1 - .../node/NodeProcessExecutionService.test.ts | 25 +- .../node/NodeThreadExecutionService.test.ts | 25 +- .../src/snaps/SnapController.test.ts | 99 +++ .../src/snaps/SnapController.ts | 16 +- .../coverage.json | 8 +- .../lavamoat/browserify/iframe/policy.json | 1 + .../browserify/node-process/policy.json | 1 + .../browserify/node-thread/policy.json | 1 + .../lavamoat/browserify/offscreen/policy.json | 7 + .../browserify/worker-executor/policy.json | 1 + .../browserify/worker-pool/policy.json | 7 + .../common/BaseSnapExecutor.test.browser.ts | 274 +++++--- .../src/common/BaseSnapExecutor.ts | 46 +- .../endowments/endowments.test.browser.ts | 1 + .../src/common/utils.test.ts | 20 - .../src/common/utils.ts | 21 - .../src/permitted/getSnaps.ts | 10 +- .../src/permitted/invokeKeyring.test.ts | 31 +- .../src/permitted/invokeKeyring.ts | 19 +- .../src/permitted/invokeSnapSugar.test.ts | 14 +- .../src/permitted/invokeSnapSugar.ts | 18 +- .../src/permitted/requestSnaps.test.ts | 2 +- .../src/permitted/requestSnaps.ts | 8 +- packages/snaps-rpc-methods/src/request.ts | 2 +- packages/snaps-simulator/package.json | 1 - .../src/features/simulation/sagas.test.ts | 49 +- .../src/features/simulation/sagas.ts | 9 +- packages/snaps-types/src/types.ts | 3 +- packages/snaps-utils/coverage.json | 8 +- packages/snaps-utils/src/errors.test.ts | 623 +++++++++++++++++- packages/snaps-utils/src/errors.ts | 420 +++++++++++- yarn.lock | 13 +- 68 files changed, 1679 insertions(+), 457 deletions(-) diff --git a/packages/examples/packages/bip32/snap.manifest.json b/packages/examples/packages/bip32/snap.manifest.json index be5698783f..361b685350 100644 --- a/packages/examples/packages/bip32/snap.manifest.json +++ b/packages/examples/packages/bip32/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "O0Ncs/BuMYDj6nUxHCvIn1rO5BkQi5o7bFSs9Yw3lH8=", + "shasum": "QbIeP2qWOCUL23DriGKPaAJBCtWiFOAsYhJJOkmTPHk=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/bip32/src/index.test.ts b/packages/examples/packages/bip32/src/index.test.ts index 8e2379f0c7..c7c5d24d2f 100644 --- a/packages/examples/packages/bip32/src/index.test.ts +++ b/packages/examples/packages/bip32/src/index.test.ts @@ -12,13 +12,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); @@ -75,15 +74,10 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: - 'The requested path is not permitted. Allowed paths must be specified in the snap manifest.', - stack: expect.any(String), - }, - }, + code: 4100, + message: + 'The requested path is not permitted. Allowed paths must be specified in the snap manifest.', + stack: expect.any(String), }); await close(); @@ -101,15 +95,10 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: - 'The requested path is not permitted. Allowed paths must be specified in the snap manifest.', - stack: expect.any(String), - }, - }, + code: 4100, + message: + 'The requested path is not permitted. Allowed paths must be specified in the snap manifest.', + stack: expect.any(String), }); await close(); @@ -202,14 +191,9 @@ describe('onRpcRequest', () => { await ui.cancel(); expect(await response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: 'User rejected the request.', - stack: expect.any(String), - }, - }, + code: 4001, + message: 'User rejected the request.', + stack: expect.any(String), }); await close(); diff --git a/packages/examples/packages/bip44/snap.manifest.json b/packages/examples/packages/bip44/snap.manifest.json index 57277d727f..3e6a6999ef 100644 --- a/packages/examples/packages/bip44/snap.manifest.json +++ b/packages/examples/packages/bip44/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "MgSpx86WLoY6ebo+Ta3qcCO9yiuIdp5mY8FlooDZJZg=", + "shasum": "jWcfzY4mlKuy+U0U5xNfKyrG0idGPeOaTNPl4Ua6uGw=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/bip44/src/index.test.ts b/packages/examples/packages/bip44/src/index.test.ts index 42ad18e6b2..deb12efa88 100644 --- a/packages/examples/packages/bip44/src/index.test.ts +++ b/packages/examples/packages/bip44/src/index.test.ts @@ -12,13 +12,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); @@ -69,15 +68,10 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: - 'The requested coin type is not permitted. Allowed coin types must be specified in the snap manifest.', - stack: expect.any(String), - }, - }, + code: 4100, + message: + 'The requested coin type is not permitted. Allowed coin types must be specified in the snap manifest.', + stack: expect.any(String), }); await close(); @@ -166,14 +160,9 @@ describe('onRpcRequest', () => { await ui.cancel(); expect(await response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: 'User rejected the request.', - stack: expect.any(String), - }, - }, + code: 4001, + message: 'User rejected the request.', + stack: expect.any(String), }); await close(); diff --git a/packages/examples/packages/browserify-plugin/src/index.test.ts b/packages/examples/packages/browserify-plugin/src/index.test.ts index 2a862aa38f..ab279c8423 100644 --- a/packages/examples/packages/browserify-plugin/src/index.test.ts +++ b/packages/examples/packages/browserify-plugin/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/browserify/src/index.test.ts b/packages/examples/packages/browserify/src/index.test.ts index 66a757d55b..f24444dcd0 100644 --- a/packages/examples/packages/browserify/src/index.test.ts +++ b/packages/examples/packages/browserify/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/dialogs/snap.manifest.json b/packages/examples/packages/dialogs/snap.manifest.json index aeee3f2c0d..c45557cc4d 100644 --- a/packages/examples/packages/dialogs/snap.manifest.json +++ b/packages/examples/packages/dialogs/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "49Ki82EdQq7D43dfQc6FSZ9BOs0gDniYJC6n5KxvoVo=", + "shasum": "5088QO3qlZ8bJEDRjmeQaQX58F0WOX/us8kjdCuVNBc=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/dialogs/src/index.test.ts b/packages/examples/packages/dialogs/src/index.test.ts index 1dfce17537..9a35473370 100644 --- a/packages/examples/packages/dialogs/src/index.test.ts +++ b/packages/examples/packages/dialogs/src/index.test.ts @@ -12,13 +12,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/ethereum-provider/src/index.test.ts b/packages/examples/packages/ethereum-provider/src/index.test.ts index a73ddb1adf..1276a78504 100644 --- a/packages/examples/packages/ethereum-provider/src/index.test.ts +++ b/packages/examples/packages/ethereum-provider/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/ethers-js/src/index.test.ts b/packages/examples/packages/ethers-js/src/index.test.ts index a1a658f65e..4e7f3debfa 100644 --- a/packages/examples/packages/ethers-js/src/index.test.ts +++ b/packages/examples/packages/ethers-js/src/index.test.ts @@ -12,13 +12,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); @@ -85,14 +84,9 @@ describe('onRpcRequest', () => { await ui.cancel(); expect(await response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', - data: { - cause: { - message: 'User rejected the request.', - stack: expect.any(String), - }, - }, + code: 4001, + message: 'User rejected the request.', + stack: expect.any(String), }); await close(); diff --git a/packages/examples/packages/get-entropy/snap.manifest.json b/packages/examples/packages/get-entropy/snap.manifest.json index a94497790b..7dc0e01e40 100644 --- a/packages/examples/packages/get-entropy/snap.manifest.json +++ b/packages/examples/packages/get-entropy/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "/vHSKNUGmy1NiS/YgXRopAXAhh5ziQuN4/G+tu8kNfQ=", + "shasum": "/ZF7EV0bS0qjAC9hZIp3guh02izXRxluYTxaMyo401I=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-entropy/src/index.test.ts b/packages/examples/packages/get-entropy/src/index.test.ts index c15e7730e2..07a310055d 100644 --- a/packages/examples/packages/get-entropy/src/index.test.ts +++ b/packages/examples/packages/get-entropy/src/index.test.ts @@ -11,13 +11,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/get-file/package.json b/packages/examples/packages/get-file/package.json index aff12021ab..fff4072a8e 100644 --- a/packages/examples/packages/get-file/package.json +++ b/packages/examples/packages/get-file/package.json @@ -31,7 +31,7 @@ "lint:dependencies": "depcheck" }, "dependencies": { - "@metamask/rpc-errors": "^5.1.1", + "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-types": "workspace:^" }, "devDependencies": { diff --git a/packages/examples/packages/get-file/snap.manifest.json b/packages/examples/packages/get-file/snap.manifest.json index a1f3738330..13974b5a03 100644 --- a/packages/examples/packages/get-file/snap.manifest.json +++ b/packages/examples/packages/get-file/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "AC9lKxpF32Lg5eUx+cl46l9hWzB940tYCWkxsCZNcIs=", + "shasum": "Ho7x2FWfVaRdRFNlgkleDb/qb/JyxlRiMfF02NU5BrM=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-file/src/index.test.ts b/packages/examples/packages/get-file/src/index.test.ts index 96d4655389..0d15c2b1aa 100644 --- a/packages/examples/packages/get-file/src/index.test.ts +++ b/packages/examples/packages/get-file/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/get-locale/snap.manifest.json b/packages/examples/packages/get-locale/snap.manifest.json index 31c0c332a6..9c2a129901 100644 --- a/packages/examples/packages/get-locale/snap.manifest.json +++ b/packages/examples/packages/get-locale/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "VDLdN2iWLu1ishmLKq0ylxJbSTaVgpMVawinDjgDNOo=", + "shasum": "2QfwCIrEzDTNhxaArUJS0XFKmZGqsZpRhR0X7ntKnNM=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/get-locale/src/index.test.ts b/packages/examples/packages/get-locale/src/index.test.ts index f75b6e0b28..933cd958a0 100644 --- a/packages/examples/packages/get-locale/src/index.test.ts +++ b/packages/examples/packages/get-locale/src/index.test.ts @@ -11,13 +11,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/invoke-snap/packages/consumer-signer/src/index.test.ts b/packages/examples/packages/invoke-snap/packages/consumer-signer/src/index.test.ts index c57163a041..45386c8a59 100644 --- a/packages/examples/packages/invoke-snap/packages/consumer-signer/src/index.test.ts +++ b/packages/examples/packages/invoke-snap/packages/consumer-signer/src/index.test.ts @@ -14,13 +14,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json index 4bce08080f..7f56c53c00 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "7eoQVjy43iBLByj9iYPD6VWPybret4NQeIQXjomeZC8=", + "shasum": "mQhJLcp7NAe1WjSsDHOH/xUyhn0LFr1Zh91g+mbhOkM=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/src/index.test.ts b/packages/examples/packages/invoke-snap/packages/core-signer/src/index.test.ts index ddc6c7da63..f9896824fe 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/src/index.test.ts +++ b/packages/examples/packages/invoke-snap/packages/core-signer/src/index.test.ts @@ -13,13 +13,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/json-rpc/src/index.test.ts b/packages/examples/packages/json-rpc/src/index.test.ts index 98f67cb57c..ac34a94b79 100644 --- a/packages/examples/packages/json-rpc/src/index.test.ts +++ b/packages/examples/packages/json-rpc/src/index.test.ts @@ -14,13 +14,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/manage-state/snap.manifest.json b/packages/examples/packages/manage-state/snap.manifest.json index aa9d6dde8a..5f4b6a6c71 100644 --- a/packages/examples/packages/manage-state/snap.manifest.json +++ b/packages/examples/packages/manage-state/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "OGmcoozWOgIrP/NOhtsXhD17TQfrXii7M7DNFbBDHhA=", + "shasum": "6NBKJ/JyEqP99JoLyUMBPQtWnxBuPchuOKe3TH4t028=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/manage-state/src/index.test.ts b/packages/examples/packages/manage-state/src/index.test.ts index ba82fddb49..687eebb59a 100644 --- a/packages/examples/packages/manage-state/src/index.test.ts +++ b/packages/examples/packages/manage-state/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/name-lookup/src/index.test.ts b/packages/examples/packages/name-lookup/src/index.test.ts index 633ee9fa1b..7062b9f61c 100644 --- a/packages/examples/packages/name-lookup/src/index.test.ts +++ b/packages/examples/packages/name-lookup/src/index.test.ts @@ -1,7 +1,7 @@ import { describe, it } from '@jest/globals'; +import type { OnNameLookupArgs } from '@metamask/snaps-types'; import { onNameLookup } from '.'; -import type { OnNameLookupArgs } from '../../../../snaps-utils/src'; const DOMAIN_MOCK = 'test.domain'; const ADDRESS_MOCK = '0xc0ffee254729296a45a3885639AC7E10F9d54979'; diff --git a/packages/examples/packages/network-access/src/index.test.ts b/packages/examples/packages/network-access/src/index.test.ts index 847e330c98..350deca164 100644 --- a/packages/examples/packages/network-access/src/index.test.ts +++ b/packages/examples/packages/network-access/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/notifications/snap.manifest.json b/packages/examples/packages/notifications/snap.manifest.json index 3fd5d2599a..5404eaf3a0 100644 --- a/packages/examples/packages/notifications/snap.manifest.json +++ b/packages/examples/packages/notifications/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "TZW+CtPEHAya0jED3VY90IvX8vCMIba1kd/zGVm/KBs=", + "shasum": "EreMy849JVhYXzo1J7iMBh0QdzEyoV3OhYMpAIbZxGs=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/notifications/src/index.test.ts b/packages/examples/packages/notifications/src/index.test.ts index f0b628f60a..fd11647312 100644 --- a/packages/examples/packages/notifications/src/index.test.ts +++ b/packages/examples/packages/notifications/src/index.test.ts @@ -11,13 +11,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/rollup-plugin/src/index.test.ts b/packages/examples/packages/rollup-plugin/src/index.test.ts index 4d14a76ccf..5bb893d685 100644 --- a/packages/examples/packages/rollup-plugin/src/index.test.ts +++ b/packages/examples/packages/rollup-plugin/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/wasm/snap.manifest.json b/packages/examples/packages/wasm/snap.manifest.json index f3ca0c5180..9b2f17c716 100644 --- a/packages/examples/packages/wasm/snap.manifest.json +++ b/packages/examples/packages/wasm/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "G6jLmVw81D5Y2tzvcbwBgxkOrcYn/hFMoruaZavY6P4=", + "shasum": "Xkej3ZcUlhETrR/jKYufasko3mFmJ4Ifq9KHAzLCJ3k=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/wasm/src/index.test.ts b/packages/examples/packages/wasm/src/index.test.ts index 52281cb7e1..1c53a8fc54 100644 --- a/packages/examples/packages/wasm/src/index.test.ts +++ b/packages/examples/packages/wasm/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/examples/packages/wasm/src/index.ts b/packages/examples/packages/wasm/src/index.ts index 0f79bd9597..41f2e2e9a4 100644 --- a/packages/examples/packages/wasm/src/index.ts +++ b/packages/examples/packages/wasm/src/index.ts @@ -80,5 +80,5 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => { return wasm[method](...params); } - throw rpcErrors.methodNotFound({ data: { request } }); + throw rpcErrors.methodNotFound({ data: { method } }); }; diff --git a/packages/examples/packages/webpack-plugin/src/index.test.ts b/packages/examples/packages/webpack-plugin/src/index.test.ts index dd4ef20378..8c3c99110c 100644 --- a/packages/examples/packages/webpack-plugin/src/index.test.ts +++ b/packages/examples/packages/webpack-plugin/src/index.test.ts @@ -10,13 +10,12 @@ describe('onRpcRequest', () => { }); expect(response).toRespondWithError({ - code: -32603, - message: 'Internal JSON-RPC error.', + code: -32601, + message: 'The method does not exist / is not available.', + stack: expect.any(String), data: { - cause: { - message: 'The method does not exist / is not available.', - stack: expect.any(String), - }, + method: 'foo', + cause: null, }, }); diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index bd56a6459d..2bbc0da9e1 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 89.51, + "branches": 89.54, "functions": 96.21, - "lines": 97.09, - "statements": 96.76 + "lines": 97.1, + "statements": 96.77 } diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index 3a810768b0..61cdfb4ad1 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -135,17 +135,5 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" - }, - "lavamoat": { - "allowScripts": { - "@metamask/permission-controller>@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>keccak": false, - "@metamask/permission-controller>@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>secp256k1": false, - "@swc/core": false, - "@wdio/browser-runner>@originjs/vite-plugin-commonjs>esbuild": false, - "@wdio/cli>@wdio/utils>edgedriver": false, - "esbuild": false, - "wdio-chromedriver-service>chromedriver": false, - "wdio-geckodriver-service>geckodriver": false - } } } diff --git a/packages/snaps-controllers/src/services/AbstractExecutionService.ts b/packages/snaps-controllers/src/services/AbstractExecutionService.ts index be2500ef3e..2138ecd79b 100644 --- a/packages/snaps-controllers/src/services/AbstractExecutionService.ts +++ b/packages/snaps-controllers/src/services/AbstractExecutionService.ts @@ -1,6 +1,7 @@ import { JsonRpcEngine } from '@metamask/json-rpc-engine'; import ObjectMultiplex from '@metamask/object-multiplex'; import type { BasePostMessageStream } from '@metamask/post-message-stream'; +import { JsonRpcError } from '@metamask/rpc-errors'; import type { SnapRpcHook, SnapRpcHookArgs } from '@metamask/snaps-utils'; import { SNAP_STREAM_NAMES, logError } from '@metamask/snaps-utils'; import type { @@ -9,12 +10,7 @@ import type { JsonRpcRequest, PendingJsonRpcResponse, } from '@metamask/utils'; -import { - Duration, - hasProperty, - isJsonRpcNotification, - isObject, -} from '@metamask/utils'; +import { Duration, isJsonRpcNotification, isObject } from '@metamask/utils'; import { createStreamMiddleware } from 'json-rpc-middleware-stream'; import { nanoid } from 'nanoid'; import { pipeline } from 'stream'; @@ -52,10 +48,6 @@ export type Job = { worker: WorkerType; }; -export class ExecutionEnvironmentError extends Error { - cause?: Json; -} - export abstract class AbstractExecutionService implements ExecutionService { @@ -387,16 +379,11 @@ export abstract class AbstractExecutionService ); if (response.error) { - const error = new ExecutionEnvironmentError(response.error.message); - if ( - isObject(response.error.data) && - hasProperty(response.error.data, 'cause') && - response.error.data.cause !== null - ) { - error.cause = response.error.data.cause; - } - - throw error; + throw new JsonRpcError( + response.error.code, + response.error.message, + response.error.data, + ); } return response.result; diff --git a/packages/snaps-controllers/src/services/browser.test.ts b/packages/snaps-controllers/src/services/browser.test.ts index a20f0ded01..caaeff1548 100644 --- a/packages/snaps-controllers/src/services/browser.test.ts +++ b/packages/snaps-controllers/src/services/browser.test.ts @@ -8,7 +8,6 @@ describe('browser entrypoint', () => { 'OffscreenExecutionService', 'WebWorkerExecutionService', 'ProxyPostMessageStream', - 'ExecutionEnvironmentError', ]; it('entrypoint has expected exports', () => { diff --git a/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts b/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts index eddc8ca799..f5136d4ec2 100644 --- a/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts +++ b/packages/snaps-controllers/src/services/node/NodeProcessExecutionService.test.ts @@ -1,8 +1,8 @@ +import { JsonRpcError } from '@metamask/rpc-errors'; import type { SnapId } from '@metamask/snaps-utils'; import { HandlerType } from '@metamask/snaps-utils'; import { createService, MOCK_BLOCK_NUMBER } from '../../test-utils'; -import { ExecutionEnvironmentError } from '../AbstractExecutionService'; import type { SnapErrorJson } from '../ExecutionService'; import { NodeProcessExecutionService } from './NodeProcessExecutionService'; @@ -72,13 +72,20 @@ describe('NodeProcessExecutionService', () => { }) .catch((error) => error); - expect(result).toBeInstanceOf(ExecutionEnvironmentError); + expect(result).toBeInstanceOf(JsonRpcError); + // @ts-expect-error - This is a `JsonRpcError`. // eslint-disable-next-line jest/prefer-strict-equal - expect((result as ExecutionEnvironmentError).cause).toEqual({ - // TODO: Unwrap errors, and change this to the actual error message. - message: 'foobar', + expect(result.serialize()).toEqual({ + code: -31001, + message: 'Wrapped Snap Error', stack: expect.any(String), + data: { + cause: { + message: 'foobar', + stack: expect.any(String), + }, + }, }); await service.terminateAllSnaps(); @@ -137,10 +144,12 @@ describe('NodeProcessExecutionService', () => { code: -32603, data: { snapId: 'TestSnap', - stack: expect.stringContaining('Error: random error inside'), + cause: { + message: 'random error inside', + stack: expect.any(String), + }, }, - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', + message: 'Unhandled Snap Error', }); await service.terminateAllSnaps(); diff --git a/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts b/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts index 437af71f8e..f4ee74890a 100644 --- a/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts +++ b/packages/snaps-controllers/src/services/node/NodeThreadExecutionService.test.ts @@ -1,8 +1,8 @@ +import { JsonRpcError } from '@metamask/rpc-errors'; import type { SnapId } from '@metamask/snaps-utils'; import { HandlerType } from '@metamask/snaps-utils'; import { createService, MOCK_BLOCK_NUMBER } from '../../test-utils'; -import { ExecutionEnvironmentError } from '../AbstractExecutionService'; import type { SnapErrorJson } from '../ExecutionService'; import { NodeThreadExecutionService } from './NodeThreadExecutionService'; @@ -72,13 +72,20 @@ describe('NodeThreadExecutionService', () => { }) .catch((error) => error); - expect(result).toBeInstanceOf(ExecutionEnvironmentError); + expect(result).toBeInstanceOf(JsonRpcError); + // @ts-expect-error - This is a `JsonRpcError`. // eslint-disable-next-line jest/prefer-strict-equal - expect((result as ExecutionEnvironmentError).cause).toEqual({ - // TODO: Unwrap errors, and change this to the actual error message. - message: 'foobar', + expect(result.serialize()).toEqual({ + code: -31001, + message: 'Wrapped Snap Error', stack: expect.any(String), + data: { + cause: { + message: 'foobar', + stack: expect.any(String), + }, + }, }); await service.terminateAllSnaps(); @@ -137,10 +144,12 @@ describe('NodeThreadExecutionService', () => { code: -32603, data: { snapId: 'TestSnap', - stack: expect.stringContaining('Error: random error inside'), + cause: { + message: 'random error inside', + stack: expect.any(String), + }, }, - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', + message: 'Unhandled Snap Error', }); await service.terminateAllSnaps(); diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.ts b/packages/snaps-controllers/src/snaps/SnapController.test.ts index bd3029b732..755fb58e24 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.test.ts @@ -2184,6 +2184,105 @@ describe('SnapController', () => { snapController.destroy(); }); + + it('crashes the Snap on unhandled errors', async () => { + const { manifest, sourceCode, svgIcon } = getSnapFiles({ + updateChecksum: true, + sourceCode: ` + module.exports.onRpcRequest = () => { + throw new Error('foo'); + }; + `, + }); + + const [snapController, service] = getSnapControllerWithEES( + getSnapControllerWithEESOptions({ + detectSnapLocation: loopbackDetect({ + manifest, + files: [sourceCode, svgIcon as VirtualFile], + }), + }), + ); + + await snapController.installSnaps(MOCK_ORIGIN, { + [MOCK_SNAP_ID]: {}, + }); + + await expect( + snapController.handleRequest({ + origin: MOCK_ORIGIN, + snapId: MOCK_SNAP_ID, + handler: HandlerType.OnRpcRequest, + request: { + jsonrpc: '2.0', + method: 'foo', + params: {}, + }, + }), + ).rejects.toThrow('foo'); + + expect(snapController.state.snaps[MOCK_SNAP_ID].status).toBe('crashed'); + + snapController.destroy(); + await service.terminateAllSnaps(); + }); + + it('does not crash the Snap on handled errors', async () => { + const { manifest, sourceCode, svgIcon } = getSnapFiles({ + updateChecksum: true, + sourceCode: ` + module.exports.onRpcRequest = () => { + class SnapError { + serialize() { + return { + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -1, + message: 'foo', + }, + }, + }; + } + } + + throw new SnapError(); + }; + `, + }); + + const [snapController, service] = getSnapControllerWithEES( + getSnapControllerWithEESOptions({ + detectSnapLocation: loopbackDetect({ + manifest, + files: [sourceCode, svgIcon as VirtualFile], + }), + }), + ); + + await snapController.installSnaps(MOCK_ORIGIN, { + [MOCK_SNAP_ID]: {}, + }); + + await expect( + snapController.handleRequest({ + origin: MOCK_ORIGIN, + snapId: MOCK_SNAP_ID, + handler: HandlerType.OnRpcRequest, + request: { + jsonrpc: '2.0', + method: 'foo', + params: {}, + }, + }), + ).rejects.toThrow('foo'); + + expect(snapController.state.snaps[MOCK_SNAP_ID].status).toBe('running'); + + snapController.destroy(); + await service.terminateAllSnaps(); + }); }); describe('installSnaps', () => { diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 3ea38f159e..1fd5b531a4 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -61,6 +61,7 @@ import { SnapStatus, SnapStatusEvents, validateFetchedSnap, + unwrapError, } from '@metamask/snaps-utils'; import type { Json, NonEmptyArray, SemVerRange } from '@metamask/utils'; import { @@ -2445,6 +2446,12 @@ export class SnapController extends BaseController< 'ExecutionService:outboundResponse', this._onOutboundResponse, ); + + this.messagingSystem.clearEventSubscriptions( + 'SnapController:snapInstalled', + ); + + this.messagingSystem.clearEventSubscriptions('SnapController:snapUpdated'); /* eslint-enable @typescript-eslint/unbound-method */ } @@ -2605,8 +2612,13 @@ export class SnapController extends BaseController< this.#recordSnapRpcRequestFinish(snapId, request.id); return result; } catch (error) { - await this.stopSnap(snapId, SnapStatusEvents.Crash); - throw error; + const [jsonRpcError, handled] = unwrapError(error); + + if (!handled) { + await this.stopSnap(snapId, SnapStatusEvents.Crash); + } + + throw jsonRpcError; } }; diff --git a/packages/snaps-execution-environments/coverage.json b/packages/snaps-execution-environments/coverage.json index 34fcf6a6bc..1899851319 100644 --- a/packages/snaps-execution-environments/coverage.json +++ b/packages/snaps-execution-environments/coverage.json @@ -1,6 +1,6 @@ { - "branches": 79.72, - "functions": 91.91, - "lines": 91.08, - "statements": 90.8 + "branches": 81.15, + "functions": 92.59, + "lines": 92.02, + "statements": 91.7 } diff --git a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json index efe12950d0..87c30906a9 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/iframe/policy.json @@ -296,6 +296,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json index e94fb225fa..0e672e2534 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-process/policy.json @@ -360,6 +360,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json index e94fb225fa..0e672e2534 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/node-thread/policy.json @@ -360,6 +360,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json index d6ecdb5cc1..eea812bb99 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/offscreen/policy.json @@ -39,6 +39,12 @@ "superstruct": true } }, + "@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -124,6 +130,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json index efe12950d0..87c30906a9 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/worker-executor/policy.json @@ -296,6 +296,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json b/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json index d6ecdb5cc1..eea812bb99 100644 --- a/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json +++ b/packages/snaps-execution-environments/lavamoat/browserify/worker-pool/policy.json @@ -39,6 +39,12 @@ "superstruct": true } }, + "@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, "@metamask/utils": { "globals": { "TextDecoder": true, @@ -124,6 +130,7 @@ }, "external:../snaps-utils/src/errors.ts": { "packages": { + "@metamask/rpc-errors": true, "@metamask/utils": true } }, diff --git a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts index 08739ff4c2..be8c5413cc 100644 --- a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts +++ b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.test.browser.ts @@ -147,13 +147,18 @@ describe('BaseSnapExecutor', () => { }); expect(await executor.readCommand()).toStrictEqual({ + jsonrpc: '2.0', + id: 2, error: { - code: -32603, - message: `The snap "${MOCK_SNAP_ID}" has been terminated during execution.`, - stack: expect.anything(), + code: -31001, + message: 'Wrapped Snap Error', + data: { + cause: expect.objectContaining({ + code: -32603, + message: `The snap "${MOCK_SNAP_ID}" has been terminated during execution.`, + }), + }, }, - id: 2, - jsonrpc: '2.0', }); }); @@ -313,16 +318,21 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'snap_confirm', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'snap_confirm', + }, + }), }, - stack: expect.any(String), }, - id: 2, }); }); @@ -354,16 +364,21 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'wallet_requestSnaps', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'wallet_requestSnaps', + }, + }), }, - stack: expect.any(String), }, - id: 2, }); }); @@ -539,13 +554,18 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32603, - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', - data: expect.any(Object), + code: -31001, + message: 'Wrapped Snap Error', + data: { + cause: expect.objectContaining({ + message: expect.stringMatching( + /Cannot read properties of undefined \(reading 'handle'\)|ethereum\._rpcEngine is undefined/u, + ), + }), + }, }, - id: 2, }); }); @@ -577,13 +597,18 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32004, - message: - 'The global Snap API only allows RPC methods starting with `wallet_*` and `snap_*`.', - stack: expect.any(String), + code: -31001, + message: 'Wrapped Snap Error', + data: { + cause: expect.objectContaining({ + code: -32004, + message: + 'The global Snap API only allows RPC methods starting with `wallet_*` and `snap_*`.', + }), + }, }, - id: 2, }); }); @@ -615,13 +640,18 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32004, - message: - 'The global Snap API only allows RPC methods starting with `wallet_*` and `snap_*`.', - stack: expect.any(String), + code: -31001, + message: 'Wrapped Snap Error', + data: { + cause: expect.objectContaining({ + code: -32004, + message: + 'The global Snap API only allows RPC methods starting with `wallet_*` and `snap_*`.', + }), + }, }, - id: 2, }); }); @@ -653,16 +683,21 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'wallet_requestSnaps', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'wallet_requestSnaps', + }, + }), }, - stack: expect.any(String), }, - id: 2, }); }); @@ -694,16 +729,21 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'snap_dialog', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'snap_dialog', + }, + }), }, - stack: expect.anything(), }, - id: 2, }); }); @@ -750,16 +790,21 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', + id: 2, error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'wallet_requestSnaps', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'wallet_requestSnaps', + }, + }), }, - stack: expect.any(String), }, - id: 2, }); }); @@ -807,13 +852,18 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', error: { - code: -32601, - message: 'The method does not exist / is not available.', + code: -31001, + message: 'Wrapped Snap Error', data: { - cause: null, - method: 'wallet_requestSnaps', + cause: expect.objectContaining({ + code: -32601, + message: 'The method does not exist / is not available.', + data: { + cause: null, + method: 'wallet_requestSnaps', + }, + }), }, - stack: expect.any(String), }, id: 2, }); @@ -1020,11 +1070,12 @@ describe('BaseSnapExecutor', () => { error: { code: -32603, data: { - stack: error.stack, snapId: MOCK_SNAP_ID, + cause: expect.objectContaining({ + message: 'Test error.', + }), }, - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', + message: 'Unhandled Snap Error', }, }, }); @@ -1081,11 +1132,12 @@ describe('BaseSnapExecutor', () => { error: { code: -32603, data: { - stack: error.stack, snapId: MOCK_SNAP_ID, + cause: expect.objectContaining({ + message: 'Test error.', + }), }, - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', + message: 'Unhandled Snap Error', }, }, }); @@ -1093,6 +1145,30 @@ describe('BaseSnapExecutor', () => { addEventListenerSpy.reset(); }); + it('throws an internal error if the Snap fails to start', async () => { + const CODE = ` + throw new Error('Failed to start.'); + `; + + const executor = new TestSnapExecutor(); + await executor.executeSnap(1, MOCK_SNAP_ID, CODE, ['ethereum']); + + expect(await executor.readCommand()).toStrictEqual({ + jsonrpc: '2.0', + id: 1, + error: expect.objectContaining({ + code: -32603, + message: `Error while running snap '${MOCK_SNAP_ID}': Failed to start.`, + data: { + cause: expect.objectContaining({ + code: -32603, + message: 'Failed to start.', + }), + }, + }), + }); + }); + it('supports onTransaction export', async () => { const CODE = ` module.exports.onTransaction = ({ transaction, chainId, transactionOrigin }) => @@ -1389,10 +1465,15 @@ describe('BaseSnapExecutor', () => { jsonrpc: '2.0', id: 2, error: { - code: -32603, - data: expect.anything(), - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', + code: -31001, + data: { + cause: expect.objectContaining({ + message: expect.stringMatching( + /Cannot read properties of undefined \(reading 'startSnap'\)|this is undefined/u, + ), + }), + }, + message: 'Wrapped Snap Error', }, }); @@ -1716,9 +1797,14 @@ describe('BaseSnapExecutor', () => { }); }); - it('throws when trying to throw an unserializable value', async () => { + it('contains the self-referential global scopes', async () => { const CODE = ` - module.exports.onRpcRequest = () => { const error = new Error("foo"); error.data = BigInt(0); throw error; }; + module.exports.onRpcRequest = () => globalThis !== undefined && + globalThis.self === self && + globalThis === self.self && + globalThis === window && + globalThis === global && + globalThis === global.global; `; const executor = new TestSnapExecutor(); @@ -1745,23 +1831,30 @@ describe('BaseSnapExecutor', () => { expect(await executor.readCommand()).toStrictEqual({ jsonrpc: '2.0', id: 2, - error: { - code: -32603, - data: expect.any(Object), - // TODO: Unwrap errors, and change this to the actual error message. - message: 'Execution Environment Error', - }, + result: true, }); }); - it('contains the self-referential global scopes', async () => { + it('handles `SnapError`s', async () => { const CODE = ` - module.exports.onRpcRequest = () => globalThis !== undefined && - globalThis.self === self && - globalThis === self.self && - globalThis === window && - globalThis === global && - globalThis === global.global; + module.exports.onRpcRequest = () => { + class SnapError { + serialize() { + return { + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -1, + message: 'Snap error message.', + }, + }, + }; + } + } + + throw new SnapError(); + }; `; const executor = new TestSnapExecutor(); @@ -1781,14 +1874,29 @@ describe('BaseSnapExecutor', () => { MOCK_SNAP_ID, HandlerType.OnRpcRequest, MOCK_ORIGIN, - { jsonrpc: '2.0', method: '', params: [] }, + { jsonrpc: '2.0', method: 'foo', params: {} }, ], }); expect(await executor.readCommand()).toStrictEqual({ - jsonrpc: '2.0', id: 2, - result: true, + jsonrpc: '2.0', + error: { + code: -31001, + message: 'Wrapped Snap Error', + data: { + cause: { + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -1, + message: 'Snap error message.', + }, + }, + }, + }, + }, }); }); diff --git a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts index 577a647f2c..ed68d60629 100644 --- a/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts +++ b/packages/snaps-execution-environments/src/common/BaseSnapExecutor.ts @@ -14,7 +14,9 @@ import { SNAP_EXPORT_NAMES, logError, SNAP_EXPORTS, - getErrorMessage, + WrappedSnapError, + getErrorData, + unwrapError, } from '@metamask/snaps-utils'; import type { JsonRpcNotification, @@ -42,7 +44,6 @@ import { sortParamKeys } from './sortParams'; import { assertEthereumOutboundRequest, assertSnapOutboundRequest, - constructError, sanitizeRequestArguments, proxyStreamProvider, withTeardown, @@ -69,6 +70,12 @@ const fallbackError = { message: 'Execution Environment Error', }; +const unhandledError = rpcErrors + .internal({ + message: 'Unhandled Snap Error', + }) + .serialize(); + export type InvokeSnapArgs = Omit; export type InvokeSnap = ( @@ -150,8 +157,8 @@ export class BaseSnapExecutor { return null; } - // TODO: fix handler args type cast let result = await this.executeInSnapContext(target, () => + // TODO: fix handler args type cast handler(args as any), ); @@ -177,21 +184,22 @@ export class BaseSnapExecutor { } private errorHandler(error: unknown, data: Record) { - const constructedError = constructError(error); - const serializedError = serializeError(constructedError, { - fallbackError, + const serializedError = serializeError(error, { + fallbackError: unhandledError, shouldIncludeStack: false, }); - // We're setting it this way to avoid sentData.stack = undefined - const sentData: Json = { ...data, stack: constructedError?.stack ?? null }; + const errorData = getErrorData(serializedError); this.notify({ method: 'UnhandledError', params: { error: { ...serializedError, - data: sentData, + data: { + ...errorData, + ...data, + }, }, }, }); @@ -272,10 +280,9 @@ export class BaseSnapExecutor { // This prevents an issue where we wouldn't respond when errors were non-serializable this.commandStream.write({ error: serializeError( - new Error('JSON-RPC responses must be JSON serializable objects.'), - { - fallbackError, - }, + rpcErrors.internal( + 'JSON-RPC responses must be JSON serializable objects.', + ), ), id, jsonrpc: '2.0', @@ -358,6 +365,7 @@ export class BaseSnapExecutor { module: snapModule, exports: snapModule.exports, }); + // All of those are JavaScript runtime specific and self referential, // but we add them for compatibility sake with external libraries. // @@ -373,14 +381,12 @@ export class BaseSnapExecutor { }); } catch (error) { this.removeSnap(snapId); + + const [cause] = unwrapError(error); throw rpcErrors.internal({ - message: `Error while running snap '${snapId}': ${getErrorMessage( - error, - )}`, + message: `Error while running snap '${snapId}': ${cause.message}`, data: { - cause: serializeError(error, { - fallbackError, - }), + cause: cause.serialize(), }, }); } @@ -535,6 +541,8 @@ export class BaseSnapExecutor { // If we didn't, we would decrease the amount of running evaluations // before the promise actually resolves return await Promise.race([executor(), stopPromise]); + } catch (error) { + throw new WrappedSnapError(error); } finally { data.runningEvaluations.delete(evaluationData); diff --git a/packages/snaps-execution-environments/src/common/endowments/endowments.test.browser.ts b/packages/snaps-execution-environments/src/common/endowments/endowments.test.browser.ts index 4e072a6590..9eea60279e 100644 --- a/packages/snaps-execution-environments/src/common/endowments/endowments.test.browser.ts +++ b/packages/snaps-execution-environments/src/common/endowments/endowments.test.browser.ts @@ -25,6 +25,7 @@ lockdown({ domainTaming: 'unsafe', errorTaming: 'unsafe', stackFiltering: 'verbose', + overrideTaming: 'severe', }); // This is a hack to make `atob`, and `btoa` hardening work. This needs to be diff --git a/packages/snaps-execution-environments/src/common/utils.test.ts b/packages/snaps-execution-environments/src/common/utils.test.ts index f601546128..b6883ccddb 100644 --- a/packages/snaps-execution-environments/src/common/utils.test.ts +++ b/packages/snaps-execution-environments/src/common/utils.test.ts @@ -2,28 +2,8 @@ import { BLOCKED_RPC_METHODS, assertEthereumOutboundRequest, assertSnapOutboundRequest, - constructError, } from './utils'; -describe('Utils', () => { - describe('constructError', () => { - it('will return the original error if it is an error', () => { - const error = constructError(new Error('unhandledrejection')); - expect(error).toStrictEqual(new Error('unhandledrejection')); - }); - - it('will return undefined if it is not passed an Error or a string', () => { - const error = constructError(undefined); - expect(error).toBeUndefined(); - }); - - it('will return an Error object with the message of the original error if it was a string', () => { - const error = constructError('some reason'); - expect(error?.message).toBe('some reason'); - }); - }); -}); - describe('assertSnapOutboundRequest', () => { it('allows wallet_ and snap_ prefixed RPC methods', () => { expect(() => diff --git a/packages/snaps-execution-environments/src/common/utils.ts b/packages/snaps-execution-environments/src/common/utils.ts index c5f3ea77e5..132fc1fd93 100644 --- a/packages/snaps-execution-environments/src/common/utils.ts +++ b/packages/snaps-execution-environments/src/common/utils.ts @@ -4,27 +4,6 @@ import { rpcErrors } from '@metamask/rpc-errors'; import { assert, assertStruct, getSafeJson, JsonStruct } from '@metamask/utils'; import { log } from '../logging'; - -/** - * Takes an error that was thrown, determines if it is - * an error object. If it is then it will return that. Otherwise, - * an error object is created with the original error message. - * - * @param originalError - The error that was originally thrown. - * @returns An error object. - */ -export function constructError(originalError: unknown) { - let _originalError: Error | undefined; - if (originalError instanceof Error) { - _originalError = originalError; - } else if (typeof originalError === 'string') { - _originalError = new Error(originalError); - // The stack is useless in this case. - delete _originalError.stack; - } - return _originalError; -} - /** * Make proxy for Promise and handle the teardown process properly. * If the teardown is called in the meanwhile, Promise result will not be diff --git a/packages/snaps-rpc-methods/src/permitted/getSnaps.ts b/packages/snaps-rpc-methods/src/permitted/getSnaps.ts index 44bd5e4711..87c627bb7b 100644 --- a/packages/snaps-rpc-methods/src/permitted/getSnaps.ts +++ b/packages/snaps-rpc-methods/src/permitted/getSnaps.ts @@ -1,9 +1,7 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; import type { InstallSnapsResult } from '@metamask/snaps-utils'; -import type { - PermittedHandlerExport, - PendingJsonRpcResponse, - JsonRpcEngineEndCallback, -} from '@metamask/types'; +import type { JsonRpcParams, PendingJsonRpcResponse } from '@metamask/utils'; import type { MethodHooksObject } from '../utils'; @@ -16,7 +14,7 @@ const hookNames: MethodHooksObject = { */ export const getSnapsHandler: PermittedHandlerExport< GetSnapsHooks, - void, + JsonRpcParams, InstallSnapsResult > = { methodNames: ['wallet_getSnaps'], diff --git a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts index 0768e6e7c4..a40862f3eb 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.test.ts @@ -4,10 +4,9 @@ import { HandlerType } from '@metamask/snaps-utils'; import { MOCK_SNAP_ID, getSnapObject } from '@metamask/snaps-utils/test-utils'; import type { JsonRpcRequest, - PendingJsonRpcResponse, JsonRpcFailure, JsonRpcSuccess, -} from '@metamask/types'; +} from '@metamask/utils'; import { invokeKeyringHandler } from './invokeKeyring'; @@ -56,8 +55,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -103,8 +102,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -151,8 +150,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -200,8 +199,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -239,8 +238,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -281,8 +280,8 @@ describe('wallet_invokeKeyring', () => { engine.push(createOriginMiddleware('metamask.io')); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, @@ -319,8 +318,8 @@ describe('wallet_invokeKeyring', () => { const engine = new JsonRpcEngine(); engine.push((req, res, next, end) => { const result = implementation( - req as JsonRpcRequest>, - res as PendingJsonRpcResponse, + req as JsonRpcRequest, + res, next, end, hooks, diff --git a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts index a0a91286bb..ea2284fc56 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeKeyring.ts @@ -1,3 +1,5 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; import type { Snap } from '@metamask/snaps-utils'; import { @@ -6,12 +8,7 @@ import { type SnapId, type SnapRpcHookArgs, } from '@metamask/snaps-utils'; -import type { - PermittedHandlerExport, - PendingJsonRpcResponse, - JsonRpcEngineEndCallback, - JsonRpcRequest, -} from '@metamask/types'; +import type { PendingJsonRpcResponse, JsonRpcRequest } from '@metamask/utils'; import { hasProperty, type Json } from '@metamask/utils'; import type { MethodHooksObject } from '../utils'; @@ -30,8 +27,8 @@ const hookNames: MethodHooksObject = { */ export const invokeKeyringHandler: PermittedHandlerExport< InvokeKeyringHooks, - JsonRpcRequest, - unknown + JsonRpcRequest, + Json > = { methodNames: ['wallet_invokeKeyring'], implementation: invokeKeyringImplementation, @@ -71,8 +68,8 @@ export type InvokeKeyringHooks = { * @returns Nothing. */ async function invokeKeyringImplementation( - req: JsonRpcRequest, - res: PendingJsonRpcResponse, + req: JsonRpcRequest, + res: PendingJsonRpcResponse, _next: unknown, end: JsonRpcEngineEndCallback, { @@ -90,7 +87,7 @@ async function invokeKeyringImplementation( } // We expect the MM middleware stack to always add the origin to requests - const { origin } = req as JsonRpcRequest & { origin: string }; + const { origin } = req as JsonRpcRequest & { origin: string }; const { snapId, request } = params; if (!origin || !hasPermission(origin, WALLET_SNAP_PERMISSION_KEY)) { diff --git a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts index d1b751289a..45f4d0ac88 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.test.ts @@ -1,16 +1,17 @@ -import { rpcErrors } from '@metamask/rpc-errors'; import type { JsonRpcEngineEndCallback, JsonRpcEngineNextCallback, - JsonRpcRequest, -} from '@metamask/types'; +} from '@metamask/json-rpc-engine'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { JsonRpcRequest } from '@metamask/utils'; +import type { InvokeSnapSugarArgs } from './invokeSnapSugar'; import { getValidatedParams, invokeSnapSugar } from './invokeSnapSugar'; describe('wallet_invokeSnap', () => { describe('invokeSnapSugar', () => { it('invokes snap with next()', () => { - const req: JsonRpcRequest = { + const req: JsonRpcRequest = { id: 'some-id', jsonrpc: '2.0', method: 'wallet_invokeSnap', @@ -31,12 +32,15 @@ describe('wallet_invokeSnap', () => { }); it('ends with an error if params are invalid', () => { - const req: JsonRpcRequest = { + const req: JsonRpcRequest = { id: 'some-id', jsonrpc: '2.0', method: 'wallet_invokeSnap', params: { + // @ts-expect-error - Invalid params. snapId: undefined, + + // @ts-expect-error - Invalid params. request: [], }, }; diff --git a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts index 7b040be79f..94f27ca0c9 100644 --- a/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts +++ b/packages/snaps-rpc-methods/src/permitted/invokeSnapSugar.ts @@ -1,15 +1,15 @@ -import { rpcErrors } from '@metamask/rpc-errors'; import type { - PermittedHandlerExport, - JsonRpcRequest, - JsonRpcEngineNextCallback, JsonRpcEngineEndCallback, -} from '@metamask/types'; + JsonRpcEngineNextCallback, +} from '@metamask/json-rpc-engine'; +import type { PermittedHandlerExport } from '@metamask/permission-controller'; +import { rpcErrors } from '@metamask/rpc-errors'; +import type { Json, JsonRpcRequest } from '@metamask/utils'; import { isObject } from '@metamask/utils'; export type InvokeSnapSugarArgs = { snapId: string; - request: Record; + request: Record; }; /** @@ -17,8 +17,8 @@ export type InvokeSnapSugarArgs = { */ export const invokeSnapSugarHandler: PermittedHandlerExport< void, - JsonRpcRequest, - unknown + InvokeSnapSugarArgs, + Json > = { methodNames: ['wallet_invokeSnap'], implementation: invokeSnapSugar, @@ -38,7 +38,7 @@ export const invokeSnapSugarHandler: PermittedHandlerExport< * @throws If the params are invalid. */ export function invokeSnapSugar( - req: JsonRpcRequest, + req: JsonRpcRequest, _res: unknown, next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, diff --git a/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts b/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts index cf47821c00..49851176b9 100644 --- a/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts +++ b/packages/snaps-rpc-methods/src/permitted/requestSnaps.test.ts @@ -15,7 +15,7 @@ import type { JsonRpcRequest, JsonRpcSuccess, PendingJsonRpcResponse, -} from '@metamask/types'; +} from '@metamask/utils'; import { WALLET_SNAP_PERMISSION_KEY } from '../restricted/invokeSnap'; import { diff --git a/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts b/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts index 085ea50063..c25bfcfc39 100644 --- a/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts +++ b/packages/snaps-rpc-methods/src/permitted/requestSnaps.ts @@ -1,7 +1,9 @@ +import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine'; import type { PermissionConstraint, RequestedPermissions, Caveat, + PermittedHandlerExport, } from '@metamask/permission-controller'; import { rpcErrors } from '@metamask/rpc-errors'; import type { SnapsPermissionRequest } from '@metamask/snaps-utils'; @@ -10,12 +12,10 @@ import { verifyRequestedSnapPermissions, } from '@metamask/snaps-utils'; import type { - PermittedHandlerExport, JsonRpcRequest, PendingJsonRpcResponse, - JsonRpcEngineEndCallback, -} from '@metamask/types'; -import type { Json } from '@metamask/utils'; + Json, +} from '@metamask/utils'; import { hasProperty, isObject } from '@metamask/utils'; import { WALLET_SNAP_PERMISSION_KEY } from '../restricted/invokeSnap'; diff --git a/packages/snaps-rpc-methods/src/request.ts b/packages/snaps-rpc-methods/src/request.ts index 46138fda75..f616a32673 100644 --- a/packages/snaps-rpc-methods/src/request.ts +++ b/packages/snaps-rpc-methods/src/request.ts @@ -2,8 +2,8 @@ import type { PermissionSpecificationBuilder, PermissionType, RestrictedMethodOptions, + PermittedHandlerExport, } from '@metamask/permission-controller'; -import type { PermittedHandlerExport } from '@metamask/types'; import type { JsonRpcParams } from '@metamask/utils'; import type { methodHandlers } from './permitted'; diff --git a/packages/snaps-simulator/package.json b/packages/snaps-simulator/package.json index ed88ed2cdf..ce3e553b20 100644 --- a/packages/snaps-simulator/package.json +++ b/packages/snaps-simulator/package.json @@ -57,7 +57,6 @@ "@metamask/json-rpc-engine": "^7.1.1", "@metamask/key-tree": "^9.0.0", "@metamask/permission-controller": "^5.0.0", - "@metamask/rpc-errors": "^6.1.0", "@metamask/snaps-controllers": "workspace:^", "@metamask/snaps-execution-environments": "workspace:^", "@metamask/snaps-rpc-methods": "workspace:^", diff --git a/packages/snaps-simulator/src/features/simulation/sagas.test.ts b/packages/snaps-simulator/src/features/simulation/sagas.test.ts index d400c97d64..0ee8ea7488 100644 --- a/packages/snaps-simulator/src/features/simulation/sagas.test.ts +++ b/packages/snaps-simulator/src/features/simulation/sagas.test.ts @@ -1,6 +1,11 @@ import type { GenericPermissionController } from '@metamask/permission-controller'; import { processSnapPermissions } from '@metamask/snaps-controllers'; -import { DEFAULT_ENDOWMENTS, HandlerType } from '@metamask/snaps-utils'; +import { + DEFAULT_ENDOWMENTS, + HandlerType, + SnapError, + WrappedSnapError, +} from '@metamask/snaps-utils'; import { expectSaga } from 'redux-saga-test-plan'; import { DEFAULT_SRP, setSnapId } from '../configuration'; @@ -80,6 +85,48 @@ describe('requestSaga', () => { }) .silentRun(); }); + + it('unwraps error responses', async () => { + const sourceCode = 'foo'; + const executionService = new MockExecutionService(); + + const error = new SnapError('foo'); + jest.spyOn(executionService, 'handleRpcRequest').mockImplementation(() => { + throw new WrappedSnapError(error); + }); + + const request = { + origin: 'Snaps Simulator', + handler: HandlerType.OnRpcRequest, + request: { + jsonrpc: '2.0', + method: 'bar', + }, + }; + + await expectSaga(requestSaga, sendRequest(request)) + .withState({ + configuration: { snapId }, + simulation: { sourceCode, executionService }, + }) + .put({ + type: `${HandlerType.OnRpcRequest}/setRequest`, + payload: request, + }) + .call([executionService, 'handleRpcRequest'], snapId, request) + .put.like({ + action: { + type: `${HandlerType.OnRpcRequest}/setResponse`, + payload: { + error: { + code: -32603, + message: 'foo', + }, + }, + }, + }) + .silentRun(); + }); }); describe('permissionsSaga', () => { diff --git a/packages/snaps-simulator/src/features/simulation/sagas.ts b/packages/snaps-simulator/src/features/simulation/sagas.ts index f99206a0ee..f9e8e3f20b 100644 --- a/packages/snaps-simulator/src/features/simulation/sagas.ts +++ b/packages/snaps-simulator/src/features/simulation/sagas.ts @@ -9,7 +9,6 @@ import { SubjectMetadataController, SubjectType, } from '@metamask/permission-controller'; -import { serializeError } from '@metamask/rpc-errors'; import { IframeExecutionService, setupMultiplex, @@ -28,7 +27,7 @@ import type { SnapRpcHookArgs, VirtualFile, } from '@metamask/snaps-utils'; -import { logError } from '@metamask/snaps-utils'; +import { logError, unwrapError } from '@metamask/snaps-utils'; import { getSafeJson } from '@metamask/utils'; import type { PayloadAction } from '@reduxjs/toolkit'; import { createEngineStream } from 'json-rpc-middleware-stream'; @@ -262,9 +261,13 @@ export function* requestSaga({ payload }: PayloadAction) { }, }); } catch (error) { + const [unwrappedError] = unwrapError(error); + yield put({ type: `${payload.handler}/setResponse`, - payload: { error: serializeError(error) }, + payload: { + error: unwrappedError.serialize(), + }, }); } } diff --git a/packages/snaps-types/src/types.ts b/packages/snaps-types/src/types.ts index 8a285fa071..69509cc83f 100644 --- a/packages/snaps-types/src/types.ts +++ b/packages/snaps-types/src/types.ts @@ -21,9 +21,10 @@ export type { OnRpcRequestHandler, OnTransactionHandler, OnTransactionResponse, + OnNameLookupArgs, OnNameLookupResponse, OnInstallHandler, OnUpdateHandler, OnKeyringRequestHandler, } from '@metamask/snaps-utils'; -export { SeverityLevel } from '@metamask/snaps-utils'; +export { SeverityLevel, SnapError } from '@metamask/snaps-utils'; diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 3fa062c4cc..bd97e71177 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { - "branches": 95.52, - "functions": 100, - "lines": 98.72, - "statements": 95.82 + "branches": 96.02, + "functions": 97.95, + "lines": 98.36, + "statements": 95.72 } diff --git a/packages/snaps-utils/src/errors.test.ts b/packages/snaps-utils/src/errors.test.ts index 930821bca9..1f3c7dd79c 100644 --- a/packages/snaps-utils/src/errors.test.ts +++ b/packages/snaps-utils/src/errors.test.ts @@ -1,6 +1,21 @@ -import { rpcErrors } from '@metamask/rpc-errors'; +import { errorCodes, JsonRpcError, rpcErrors } from '@metamask/rpc-errors'; -import { getErrorMessage } from './errors'; +import { + getErrorCode, + getErrorData, + getErrorMessage, + getErrorStack, + isSerializedSnapError, + isSnapError, + isWrappedSnapError, + SNAP_ERROR_CODE, + SNAP_ERROR_MESSAGE, + SNAP_ERROR_WRAPPER_CODE, + SNAP_ERROR_WRAPPER_MESSAGE, + SnapError, + unwrapError, + WrappedSnapError, +} from './errors'; describe('getErrorMessage', () => { it('returns the error message if the error is an object with a message property', () => { @@ -18,3 +33,607 @@ describe('getErrorMessage', () => { expect(getErrorMessage({ foo: 'bar' })).toBe('[object Object]'); }); }); + +describe('getErrorStack', () => { + it('returns the error stack if the error is an object with a stack property', () => { + const error = new Error('foo'); + + expect(getErrorStack(error)).toBe(error.stack); + expect(getErrorStack({ stack: 'foo' })).toBe('foo'); + expect(getErrorStack(rpcErrors.invalidParams('foo'))).toBeDefined(); + }); + + it('returns undefined if the error does not have a stack property', () => { + expect(getErrorStack('foo')).toBeUndefined(); + expect(getErrorStack(123)).toBeUndefined(); + expect(getErrorStack(true)).toBeUndefined(); + expect(getErrorStack(null)).toBeUndefined(); + expect(getErrorStack(undefined)).toBeUndefined(); + expect(getErrorStack({ foo: 'bar' })).toBeUndefined(); + }); +}); + +describe('getErrorCode', () => { + it('returns the error code if the error is an object with a code property', () => { + expect(getErrorCode({ code: 123 })).toBe(123); + expect(getErrorCode(rpcErrors.invalidParams('foo'))).toBe(-32602); + }); + + it('returns `errorCodes.rpc.internal` if the error does not have a code property', () => { + expect(getErrorCode('foo')).toBe(errorCodes.rpc.internal); + expect(getErrorCode(123)).toBe(errorCodes.rpc.internal); + expect(getErrorCode(true)).toBe(errorCodes.rpc.internal); + expect(getErrorCode(null)).toBe(errorCodes.rpc.internal); + expect(getErrorCode(undefined)).toBe(errorCodes.rpc.internal); + expect(getErrorCode({ foo: 'bar' })).toBe(errorCodes.rpc.internal); + }); +}); + +describe('getErrorData', () => { + it('returns the error data if the error is an object with a data property', () => { + expect(getErrorData({ data: { foo: 'bar' } })).toStrictEqual({ + foo: 'bar', + }); + + expect(getErrorData(rpcErrors.invalidParams('foo'))).toStrictEqual({}); + }); + + it('returns an empty object if the error does not have a data property', () => { + expect(getErrorData('foo')).toStrictEqual({}); + expect(getErrorData(123)).toStrictEqual({}); + expect(getErrorData(true)).toStrictEqual({}); + expect(getErrorData(null)).toStrictEqual({}); + expect(getErrorData(undefined)).toStrictEqual({}); + expect(getErrorData({ foo: 'bar' })).toStrictEqual({}); + }); +}); + +describe('WrappedSnapError', () => { + it('wraps an error', () => { + const error = new Error('foo'); + const wrapped = new WrappedSnapError(error); + + expect(wrapped).toBeInstanceOf(Error); + expect(wrapped).toBeInstanceOf(WrappedSnapError); + expect(wrapped.name).toBe('WrappedSnapError'); + expect(wrapped.message).toBe('foo'); + expect(wrapped.stack).toBeDefined(); + expect(wrapped.toJSON()).toStrictEqual({ + code: SNAP_ERROR_WRAPPER_CODE, + message: SNAP_ERROR_WRAPPER_MESSAGE, + data: { + cause: { + message: 'foo', + stack: error.stack, + }, + }, + }); + }); + + it('wraps a JSON-RPC error', () => { + const error = new JsonRpcError(-1, 'foo'); + const wrapped = new WrappedSnapError(error); + + expect(wrapped).toBeInstanceOf(Error); + expect(wrapped).toBeInstanceOf(WrappedSnapError); + expect(wrapped.name).toBe('WrappedSnapError'); + expect(wrapped.message).toBe('foo'); + expect(wrapped.stack).toBeDefined(); + expect(wrapped.toJSON()).toStrictEqual({ + code: SNAP_ERROR_WRAPPER_CODE, + message: SNAP_ERROR_WRAPPER_MESSAGE, + data: { + cause: { + code: -1, + message: 'foo', + stack: error.stack, + }, + }, + }); + }); + + it('wraps a Snap error', () => { + const error = new SnapError('foo'); + const wrapped = new WrappedSnapError(error); + + expect(wrapped).toBeInstanceOf(Error); + expect(wrapped).toBeInstanceOf(WrappedSnapError); + expect(wrapped.message).toBe('foo'); + expect(wrapped.stack).toBeDefined(); + expect(wrapped.toJSON()).toStrictEqual({ + code: SNAP_ERROR_WRAPPER_CODE, + message: SNAP_ERROR_WRAPPER_MESSAGE, + data: { + cause: { + code: SNAP_ERROR_CODE, + message: SNAP_ERROR_MESSAGE, + data: { + cause: { + code: -32603, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }, + }, + }); + }); + + describe('serialize', () => { + it('serializes the wrapped error', () => { + const error = new SnapError('foo'); + const wrapped = new WrappedSnapError(error); + + expect(wrapped.serialize()).toStrictEqual({ + code: SNAP_ERROR_WRAPPER_CODE, + message: SNAP_ERROR_WRAPPER_MESSAGE, + data: { + cause: { + code: SNAP_ERROR_CODE, + message: SNAP_ERROR_MESSAGE, + data: { + cause: { + code: -32603, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }, + }, + }); + }); + }); +}); + +describe('SnapError', () => { + it('creates an error from a message', () => { + const error = new SnapError('foo'); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32603); + expect(error.data).toStrictEqual({}); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32603, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }); + }); + + it('creates an error from a message and code', () => { + const error = new SnapError({ + message: 'foo', + code: -32000, + }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32000); + expect(error.data).toStrictEqual({}); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32000, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }); + }); + + it('creates an error from a message and data', () => { + const error = new SnapError('foo', { foo: 'bar' }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32603); + expect(error.data).toStrictEqual({ foo: 'bar' }); + expect(error.stack).toBeDefined(); + }); + + it('creates an error from a message, code, and data', () => { + const error = new SnapError( + { + message: 'foo', + code: -32000, + }, + { foo: 'bar' }, + ); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32000); + expect(error.data).toStrictEqual({ foo: 'bar' }); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32000, + message: 'foo', + stack: error.stack, + data: { + foo: 'bar', + }, + }, + }, + }); + }); + + it('creates an error from an error', () => { + const error = new SnapError(new Error('foo')); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32603); + expect(error.data).toStrictEqual({}); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32603, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }); + }); + + it('creates an error from an error and data', () => { + const error = new SnapError(new Error('foo'), { foo: 'bar' }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32603); + expect(error.data).toStrictEqual({ foo: 'bar' }); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32603, + message: 'foo', + stack: error.stack, + data: { + foo: 'bar', + }, + }, + }, + }); + }); + + it('creates an error from a JsonRpcError', () => { + const error = new SnapError(rpcErrors.invalidParams('foo')); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32602); + expect(error.data).toStrictEqual({}); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32602, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }); + }); + + it('creates an error from a JsonRpcError and data', () => { + const error = new SnapError(rpcErrors.invalidParams('foo'), { + foo: 'bar', + }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(-32602); + expect(error.data).toStrictEqual({ foo: 'bar' }); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: -32602, + message: 'foo', + stack: error.stack, + data: { + foo: 'bar', + }, + }, + }, + }); + }); + + it('creates an error from a JsonRpcError with a code of 0', () => { + const error = new SnapError({ + message: 'foo', + code: 0, + }); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(0); + expect(error.data).toStrictEqual({}); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: 0, + message: 'foo', + stack: error.stack, + data: {}, + }, + }, + }); + }); + + it('creates an error from a JsonRpcError with a code of 0 and data', () => { + const error = new SnapError( + { + message: 'foo', + code: 0, + }, + { foo: 'bar' }, + ); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(0); + expect(error.data).toStrictEqual({ foo: 'bar' }); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: 0, + message: 'foo', + stack: error.stack, + data: { + foo: 'bar', + }, + }, + }, + }); + }); + + it('creates an error from a JsonRpcError with a code of 0 and merges the data', () => { + const error = new SnapError( + { + message: 'foo', + code: 0, + data: { + foo: 'baz', + bar: 'qux', + }, + }, + { foo: 'bar' }, + ); + + expect(error).toBeInstanceOf(Error); + expect(error).toBeInstanceOf(SnapError); + expect(error.message).toBe('foo'); + expect(error.code).toBe(0); + expect(error.data).toStrictEqual({ foo: 'bar', bar: 'qux' }); + expect(error.stack).toBeDefined(); + expect(error.toJSON()).toStrictEqual({ + code: -31002, + message: 'Snap Error', + data: { + cause: { + code: 0, + message: 'foo', + stack: error.stack, + data: { + foo: 'bar', + bar: 'qux', + }, + }, + }, + }); + }); +}); + +describe('isSnapError', () => { + it('returns true if the error is a Snap error', () => { + const error = new SnapError('foo'); + + expect(isSnapError(error)).toBe(true); + }); + + it('returns false if the error is not a Snap error', () => { + const error = new Error('foo'); + + expect(isSnapError(error)).toBe(false); + }); +}); + +describe('isSerializedSnapError', () => { + it('returns true if the error is a serialized Snap error', () => { + const error = new SnapError('foo').toJSON(); + + expect(isSerializedSnapError(error)).toBe(true); + }); + + it('returns false if the error is not a serialized Snap error', () => { + const error = { + code: -32603, + message: 'foo', + }; + + expect(isSerializedSnapError(error)).toBe(false); + }); +}); + +describe('isWrappedSnapError', () => { + it('returns true if the error is a Snap error wrapper', () => { + const error = new WrappedSnapError(new Error('foo')).toJSON(); + + expect(isWrappedSnapError(error)).toBe(true); + }); + + it('returns false if the error is not a Snap error wrapper', () => { + const error = new Error('foo'); + + expect(isWrappedSnapError(error)).toBe(false); + }); +}); + +describe('unwrapError', () => { + it('unwraps a wrapped Snap error with an unknown error', () => { + const error = new Error('foo'); + const wrapped = new WrappedSnapError(error).toJSON(); + + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(errorCodes.rpc.internal); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); + + it('unwraps a wrapped Snap error with a JSON-RPC error', () => { + const error = new JsonRpcError(-1, 'foo'); + const wrapped = new WrappedSnapError(error).toJSON(); + + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(-1); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); + + it('unwraps a wrapped Snap error with a `rpc-errors` error', () => { + const error = rpcErrors.invalidParams('foo'); + const wrapped = new WrappedSnapError(error).toJSON(); + + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(errorCodes.rpc.invalidParams); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); + + it('unwraps a wrapped Snap error with a wrapped Snap error', () => { + const error = new SnapError('foo'); + const wrapped = new WrappedSnapError(error).toJSON(); + + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(-32603); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(true); + }); + + it('unwraps a wrapped Snap error with a wrapped Snap error and code', () => { + const error = new SnapError({ + message: 'foo', + code: -32000, + }); + + const wrapped = new WrappedSnapError(error).toJSON(); + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(-32000); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(true); + }); + + it('unwraps a wrapped Snap error with a wrapped Snap error and data', () => { + const error = new SnapError('foo', { foo: 'bar' }); + + const wrapped = new WrappedSnapError(error).toJSON(); + const [unwrappedError, handled] = unwrapError(wrapped); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(-32603); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(unwrappedError.data).toStrictEqual({ + foo: 'bar', + }); + expect(handled).toBe(true); + }); + + it('unwraps an unwrapped JSON-RPC error', () => { + const error = new JsonRpcError(-1, 'foo'); + + const [unwrappedError, handled] = unwrapError(error); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(-1); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); + + it('unwraps an unwrapped `rpc-errors` error', () => { + const error = rpcErrors.invalidParams('foo'); + + const [unwrappedError, handled] = unwrapError(error); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(errorCodes.rpc.invalidParams); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); + + it('unwraps an unknown error', () => { + const error = new Error('foo'); + + const [unwrappedError, handled] = unwrapError(error); + + expect(unwrappedError).toBeInstanceOf(Error); + expect(unwrappedError.code).toBe(errorCodes.rpc.internal); + expect(unwrappedError.message).toBe('foo'); + expect(unwrappedError.stack).toBeDefined(); + expect(handled).toBe(false); + }); +}); diff --git a/packages/snaps-utils/src/errors.ts b/packages/snaps-utils/src/errors.ts index c7eddcc818..2423a97a67 100644 --- a/packages/snaps-utils/src/errors.ts +++ b/packages/snaps-utils/src/errors.ts @@ -1,4 +1,16 @@ -import { hasProperty, isObject } from '@metamask/utils'; +import { + errorCodes, + JsonRpcError as RpcError, + serializeCause, +} from '@metamask/rpc-errors'; +import type { DataWithOptionalCause } from '@metamask/rpc-errors'; +import type { Json, JsonRpcError } from '@metamask/utils'; +import { + hasProperty, + isJsonRpcError, + isObject, + isValidJson, +} from '@metamask/utils'; /** * Get the error message from an unknown error type. @@ -20,3 +32,409 @@ export function getErrorMessage(error: unknown) { return String(error); } + +/** + * Get the error stack from an unknown error type. + * + * @param error - The error to get the stack from. + * @returns The error stack, or undefined if the error does not have a valid + * stack. + */ +export function getErrorStack(error: unknown) { + if ( + isObject(error) && + hasProperty(error, 'stack') && + typeof error.stack === 'string' + ) { + return error.stack; + } + + return undefined; +} + +/** + * Get the error code from an unknown error type. + * + * @param error - The error to get the code from. + * @returns The error code, or `-32603` if the error does not have a valid code. + */ +export function getErrorCode(error: unknown) { + if ( + isObject(error) && + hasProperty(error, 'code') && + typeof error.code === 'number' && + Number.isInteger(error.code) + ) { + return error.code; + } + + return errorCodes.rpc.internal; +} + +/** + * Get the error data from an unknown error type. + * + * @param error - The error to get the data from. + * @returns The error data, or an empty object if the error does not have valid + * data. + */ +export function getErrorData(error: unknown) { + if ( + isObject(error) && + hasProperty(error, 'data') && + typeof error.data === 'object' && + error.data !== null && + isValidJson(error.data) && + !Array.isArray(error.data) + ) { + return error.data; + } + + return {}; +} + +export const SNAP_ERROR_WRAPPER_CODE = -31001; +export const SNAP_ERROR_WRAPPER_MESSAGE = 'Wrapped Snap Error'; + +export const SNAP_ERROR_CODE = -31002; +export const SNAP_ERROR_MESSAGE = 'Snap Error'; + +export type SerializedSnapErrorWrapper = { + code: typeof SNAP_ERROR_WRAPPER_CODE; + message: typeof SNAP_ERROR_WRAPPER_MESSAGE; + data: { + cause: Json; + }; +}; + +export type SerializedSnapError = { + code: typeof SNAP_ERROR_CODE; + message: typeof SNAP_ERROR_MESSAGE; + data: { + cause: JsonRpcError & { + data: Record; + }; + }; +}; + +export class WrappedSnapError extends Error { + readonly #error: unknown; + + readonly #message: string; + + readonly #stack?: string; + + /** + * Create a new `WrappedSnapError`. + * + * @param error - The error to create the `WrappedSnapError` from. + */ + constructor(error: unknown) { + const message = getErrorMessage(error); + super(message); + + this.#error = error; + this.#message = message; + this.#stack = getErrorStack(error); + } + + /** + * The error name. + * + * @returns The error name. + */ + get name() { + return 'WrappedSnapError'; + } + + /** + * The error message. + * + * @returns The error message. + */ + get message() { + return this.#message; + } + + /** + * The error stack. + * + * @returns The error stack. + */ + get stack() { + return this.#stack; + } + + /** + * Convert the error to a JSON object. + * + * @returns The JSON object. + */ + toJSON(): SerializedSnapErrorWrapper { + const cause = isSnapError(this.#error) + ? this.#error.serialize() + : serializeCause(this.#error); + + return { + code: SNAP_ERROR_WRAPPER_CODE, + message: SNAP_ERROR_WRAPPER_MESSAGE, + data: { + cause, + }, + }; + } + + /** + * Serialize the error to a JSON object. This is called by + * `@metamask/rpc-errors` when serializing the error. + * + * @returns The JSON object. + */ + serialize() { + return this.toJSON(); + } +} + +/** + * A generic error which can be thrown by a Snap, without it causing the Snap to + * crash. + */ +export class SnapError extends Error { + readonly #code: number; + + readonly #message: string; + + readonly #data: Record; + + readonly #stack?: string; + + /** + * Create a new `SnapError`. + * + * @param error - The error to create the `SnapError` from. If this is a + * `string`, it will be used as the error message. If this is an `Error`, its + * `message` property will be used as the error message. If this is a + * `JsonRpcError`, its `message` property will be used as the error message + * and its `code` property will be used as the error code. Otherwise, the + * error will be converted to a string and used as the error message. + * @param data - Additional data to include in the error. This will be merged + * with the error data, if any. + */ + constructor( + error: string | Error | JsonRpcError, + data: Record = {}, + ) { + const message = getErrorMessage(error); + super(message); + + this.#message = message; + this.#code = getErrorCode(error); + this.#data = { ...getErrorData(error), ...data }; + this.#stack = super.stack; + } + + /** + * The error name. + * + * @returns The error name. + */ + get name() { + return 'SnapError'; + } + + /** + * The error code. + * + * @returns The error code. + */ + get code() { + return this.#code; + } + + /** + * The error message. + * + * @returns The error message. + */ + get message() { + return this.#message; + } + + /** + * Additional data for the error. + * + * @returns Additional data for the error. + */ + get data() { + return this.#data; + } + + /** + * The error stack. + * + * @returns The error stack. + */ + get stack() { + return this.#stack; + } + + /** + * Convert the error to a JSON object. + * + * @returns The JSON object. + */ + toJSON(): SerializedSnapError { + return { + code: SNAP_ERROR_CODE, + message: SNAP_ERROR_MESSAGE, + data: { + cause: { + code: this.code, + message: this.message, + stack: this.stack, + data: this.data, + }, + }, + }; + } + + /** + * Serialize the error to a JSON object. This is called by + * `@metamask/rpc-errors` when serializing the error. + * + * @returns The JSON object. + */ + serialize() { + return this.toJSON(); + } +} + +/** + * Check if an object is a `SnapError`. + * + * @param error - The object to check. + * @returns Whether the object is a `SnapError`. + */ +export function isSnapError(error: unknown): error is SnapError { + if ( + isObject(error) && + 'serialize' in error && + typeof error.serialize === 'function' + ) { + const serialized = error.serialize(); + return isJsonRpcError(serialized) && isSerializedSnapError(serialized); + } + + return false; +} + +/** + * Check if a JSON-RPC error is a `SnapError`. + * + * @param error - The object to check. + * @returns Whether the object is a `SnapError`. + */ +export function isSerializedSnapError( + error: JsonRpcError, +): error is SerializedSnapError { + return error.code === SNAP_ERROR_CODE && error.message === SNAP_ERROR_MESSAGE; +} + +/** + * Check if a JSON-RPC error is a `WrappedSnapError`. + * + * @param error - The object to check. + * @returns Whether the object is a `WrappedSnapError`. + */ +export function isWrappedSnapError( + error: unknown, +): error is SerializedSnapErrorWrapper { + return ( + isJsonRpcError(error) && + error.code === SNAP_ERROR_WRAPPER_CODE && + error.message === SNAP_ERROR_WRAPPER_MESSAGE + ); +} + +/** + * Get a JSON-RPC error with the given code, message, stack, and data. + * + * @param code - The error code. + * @param message - The error message. + * @param stack - The error stack. + * @param data - Additional data for the error. + * @returns The JSON-RPC error. + */ +function getJsonRpcError( + code: number, + message: string, + stack?: string, + data?: Json, +) { + const error = new RpcError(code, message, data); + error.stack = stack; + + return error; +} + +/** + * Attempt to unwrap an unknown error to a `JsonRpcError`. This function will + * try to get the error code, message, and data from the error, and return a + * `JsonRpcError` with those properties. + * + * @param error - The error to unwrap. + * @returns A tuple containing the unwrapped error and a boolean indicating + * whether the error was handled. + */ +export function unwrapError( + error: unknown, +): [error: RpcError, isHandled: boolean] { + // This logic is a bit complicated, but it's necessary to handle all the + // different types of errors that can be thrown by a Snap. + + // If the error is a wrapped Snap error, unwrap it. + if (isWrappedSnapError(error)) { + // The wrapped error can be a JSON-RPC error, or an unknown error. If it's + // a JSON-RPC error, we can unwrap it further. + if (isJsonRpcError(error.data.cause)) { + // If the JSON-RPC error is a wrapped Snap error, unwrap it further. + if (isSerializedSnapError(error.data.cause)) { + const { code, message, stack, data } = error.data.cause.data.cause; + return [getJsonRpcError(code, message, stack, data), true]; + } + + // Otherwise, we use the original JSON-RPC error. + const { code, message, stack, data } = error.data.cause; + return [getJsonRpcError(code, message, stack, data), false]; + } + + // Otherwise, we throw an internal error with the wrapped error as the + // message. + return [ + getJsonRpcError( + errorCodes.rpc.internal, + getErrorMessage(error.data.cause), + getErrorStack(error.data.cause), + ), + false, + ]; + } + + // The error can be a non-wrapped JSON-RPC error, in which case we can just + // re-throw it with the same code, message, and data. + if (isJsonRpcError(error)) { + const { code, message, stack, data } = error; + return [getJsonRpcError(code, message, stack, data), false]; + } + + // If the error is not a wrapped error, we don't know how to handle it, so we + // throw an internal error with the error as the message. + return [ + getJsonRpcError( + errorCodes.rpc.internal, + getErrorMessage(error), + getErrorStack(error), + ), + false, + ]; +} diff --git a/yarn.lock b/yarn.lock index 92a90d92a1..414f973e03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4469,7 +4469,7 @@ __metadata: "@metamask/eslint-config-jest": ^12.1.0 "@metamask/eslint-config-nodejs": ^12.1.0 "@metamask/eslint-config-typescript": ^12.1.0 - "@metamask/rpc-errors": ^5.1.1 + "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-cli": "workspace:^" "@metamask/snaps-jest": "workspace:^" "@metamask/snaps-types": "workspace:^" @@ -4952,16 +4952,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/rpc-errors@npm:^5.1.1": - version: 5.1.1 - resolution: "@metamask/rpc-errors@npm:5.1.1" - dependencies: - "@metamask/utils": ^5.0.0 - fast-safe-stringify: ^2.0.6 - checksum: ccd1b24da66af3ae63960b79c04b86efb8b96acb89ca6f7e0bbfe636d23ba5cddeba533c0692eafb87c44ec6f840085372d0f21b39e05df9a80700ff61538a30 - languageName: node - linkType: hard - "@metamask/rpc-errors@npm:^6.0.0, @metamask/rpc-errors@npm:^6.1.0": version: 6.1.0 resolution: "@metamask/rpc-errors@npm:6.1.0" @@ -5463,7 +5453,6 @@ __metadata: "@metamask/json-rpc-engine": ^7.1.1 "@metamask/key-tree": ^9.0.0 "@metamask/permission-controller": ^5.0.0 - "@metamask/rpc-errors": ^6.1.0 "@metamask/snaps-controllers": "workspace:^" "@metamask/snaps-execution-environments": "workspace:^" "@metamask/snaps-rpc-methods": "workspace:^" From e4dae2d919280c0762828d02265cc841c5978748 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 20 Oct 2023 15:54:59 +0200 Subject: [PATCH 3/4] Remove dependency on "@metamask/snaps-execution-environments" --- packages/snaps-controllers/package.json | 1 - yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index 61cdfb4ad1..2876081884 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -49,7 +49,6 @@ "@metamask/permission-controller": "^5.0.0", "@metamask/post-message-stream": "^7.0.0", "@metamask/rpc-errors": "^6.1.0", - "@metamask/snaps-execution-environments": "workspace:^", "@metamask/snaps-registry": "^2.1.0", "@metamask/snaps-rpc-methods": "workspace:^", "@metamask/snaps-utils": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 414f973e03..bd8cde2c18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5134,7 +5134,6 @@ __metadata: "@metamask/permission-controller": ^5.0.0 "@metamask/post-message-stream": ^7.0.0 "@metamask/rpc-errors": ^6.1.0 - "@metamask/snaps-execution-environments": "workspace:^" "@metamask/snaps-registry": ^2.1.0 "@metamask/snaps-rpc-methods": "workspace:^" "@metamask/snaps-utils": "workspace:^" From c9a9288bed7a713fb0f6ea3d520c3c44ec644158 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 20 Oct 2023 15:55:45 +0200 Subject: [PATCH 4/4] Remove old comment --- .../snaps-controllers/src/services/AbstractExecutionService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/snaps-controllers/src/services/AbstractExecutionService.ts b/packages/snaps-controllers/src/services/AbstractExecutionService.ts index 2138ecd79b..9fe8a27102 100644 --- a/packages/snaps-controllers/src/services/AbstractExecutionService.ts +++ b/packages/snaps-controllers/src/services/AbstractExecutionService.ts @@ -219,9 +219,7 @@ export abstract class AbstractExecutionService jobId: string, ): Promise<{ streams: JobStreams; worker: WorkerType }> { const { worker, stream: envStream } = await this.initEnvStream(jobId); - // Typecast justification: stream type mismatch const mux = setupMultiplex(envStream, `Job: "${jobId}"`); - const commandStream = mux.createStream(SNAP_STREAM_NAMES.COMMAND); // Handle out-of-band errors, i.e. errors thrown from the snap outside of the req/res cycle.