diff --git a/.gitignore b/.gitignore
index 855ae66df29d..467e8b139aae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,5 +81,6 @@ licenseInfos.json
# API Spec tests
html-report/
html-report-multichain/
+html-report-caip27/
/changed-files
diff --git a/app/scripts/lib/multichain-api/scope/assert.ts b/app/scripts/lib/multichain-api/scope/assert.ts
index 9cb1e2d6373b..0908a1a9d1ba 100644
--- a/app/scripts/lib/multichain-api/scope/assert.ts
+++ b/app/scripts/lib/multichain-api/scope/assert.ts
@@ -24,6 +24,7 @@ export const assertScopeSupported = (
const allMethodsSupported = methods.every((method) =>
isSupportedMethod(scopeString, method),
);
+
if (!allMethodsSupported) {
// not sure which one of these to use
// When provider evaluates requested methods to not be supported
diff --git a/html-report-caip27/index.html b/html-report-caip27/index.html
new file mode 100644
index 000000000000..e3a023d1158d
--- /dev/null
+++ b/html-report-caip27/index.html
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+ OpenRPC API Test HTML Reporter
+
+
+
+
+
+
+
diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json
index 911a6ff8b04a..7319566d5cd2 100644
--- a/lavamoat/browserify/beta/policy.json
+++ b/lavamoat/browserify/beta/policy.json
@@ -1459,18 +1459,13 @@
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/eth-sig-util": true,
- "@metamask/message-manager>jsonschema": true,
"@metamask/utils": true,
"browserify>buffer": true,
+ "jsonschema": true,
"uuid": true,
"webpack>events": true
}
},
- "@metamask/message-manager>jsonschema": {
- "packages": {
- "browserify>url": true
- }
- },
"@metamask/message-signing-snap>@noble/ciphers": {
"globals": {
"TextDecoder": true,
@@ -2939,6 +2934,62 @@
"crypto": true
}
},
+ "@open-rpc/schema-utils-js": {
+ "packages": {
+ "@open-rpc/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "@open-rpc/schema-utils-js>ajv": true,
+ "@open-rpc/schema-utils-js>is-url": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true,
+ "@open-rpc/test-coverage>isomorphic-fetch": true
+ }
+ },
+ "@open-rpc/schema-utils-js>ajv": {
+ "globals": {
+ "console": true
+ },
+ "packages": {
+ "@metamask/snaps-utils>fast-json-stable-stringify": true,
+ "@open-rpc/schema-utils-js>ajv>json-schema-traverse": true,
+ "eslint>ajv>uri-js": true,
+ "eslint>fast-deep-equal": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch": {
+ "globals": {
+ "fetch.bind": true
+ },
+ "packages": {
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": {
+ "globals": {
+ "AbortController": true,
+ "Blob": true,
+ "FileReader": true,
+ "FormData": true,
+ "URLSearchParams.prototype.isPrototypeOf": true,
+ "XMLHttpRequest": true,
+ "console.warn": true,
+ "define": true,
+ "setTimeout": true
+ }
+ },
"@popperjs/core": {
"globals": {
"Element": true,
@@ -3835,6 +3886,11 @@
"koa>is-generator-function>has-tostringtag": true
}
},
+ "eslint>ajv>uri-js": {
+ "globals": {
+ "define": true
+ }
+ },
"eth-ens-namehash": {
"globals": {
"name": "write"
@@ -4571,6 +4627,11 @@
"readable-stream": true
}
},
+ "jsonschema": {
+ "packages": {
+ "browserify>url": true
+ }
+ },
"koa>content-disposition>safe-buffer": {
"packages": {
"browserify>buffer": true
diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json
index 911a6ff8b04a..7319566d5cd2 100644
--- a/lavamoat/browserify/flask/policy.json
+++ b/lavamoat/browserify/flask/policy.json
@@ -1459,18 +1459,13 @@
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/eth-sig-util": true,
- "@metamask/message-manager>jsonschema": true,
"@metamask/utils": true,
"browserify>buffer": true,
+ "jsonschema": true,
"uuid": true,
"webpack>events": true
}
},
- "@metamask/message-manager>jsonschema": {
- "packages": {
- "browserify>url": true
- }
- },
"@metamask/message-signing-snap>@noble/ciphers": {
"globals": {
"TextDecoder": true,
@@ -2939,6 +2934,62 @@
"crypto": true
}
},
+ "@open-rpc/schema-utils-js": {
+ "packages": {
+ "@open-rpc/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "@open-rpc/schema-utils-js>ajv": true,
+ "@open-rpc/schema-utils-js>is-url": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true,
+ "@open-rpc/test-coverage>isomorphic-fetch": true
+ }
+ },
+ "@open-rpc/schema-utils-js>ajv": {
+ "globals": {
+ "console": true
+ },
+ "packages": {
+ "@metamask/snaps-utils>fast-json-stable-stringify": true,
+ "@open-rpc/schema-utils-js>ajv>json-schema-traverse": true,
+ "eslint>ajv>uri-js": true,
+ "eslint>fast-deep-equal": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch": {
+ "globals": {
+ "fetch.bind": true
+ },
+ "packages": {
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": {
+ "globals": {
+ "AbortController": true,
+ "Blob": true,
+ "FileReader": true,
+ "FormData": true,
+ "URLSearchParams.prototype.isPrototypeOf": true,
+ "XMLHttpRequest": true,
+ "console.warn": true,
+ "define": true,
+ "setTimeout": true
+ }
+ },
"@popperjs/core": {
"globals": {
"Element": true,
@@ -3835,6 +3886,11 @@
"koa>is-generator-function>has-tostringtag": true
}
},
+ "eslint>ajv>uri-js": {
+ "globals": {
+ "define": true
+ }
+ },
"eth-ens-namehash": {
"globals": {
"name": "write"
@@ -4571,6 +4627,11 @@
"readable-stream": true
}
},
+ "jsonschema": {
+ "packages": {
+ "browserify>url": true
+ }
+ },
"koa>content-disposition>safe-buffer": {
"packages": {
"browserify>buffer": true
diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json
index 911a6ff8b04a..7319566d5cd2 100644
--- a/lavamoat/browserify/main/policy.json
+++ b/lavamoat/browserify/main/policy.json
@@ -1459,18 +1459,13 @@
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/eth-sig-util": true,
- "@metamask/message-manager>jsonschema": true,
"@metamask/utils": true,
"browserify>buffer": true,
+ "jsonschema": true,
"uuid": true,
"webpack>events": true
}
},
- "@metamask/message-manager>jsonschema": {
- "packages": {
- "browserify>url": true
- }
- },
"@metamask/message-signing-snap>@noble/ciphers": {
"globals": {
"TextDecoder": true,
@@ -2939,6 +2934,62 @@
"crypto": true
}
},
+ "@open-rpc/schema-utils-js": {
+ "packages": {
+ "@open-rpc/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "@open-rpc/schema-utils-js>ajv": true,
+ "@open-rpc/schema-utils-js>is-url": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true,
+ "@open-rpc/test-coverage>isomorphic-fetch": true
+ }
+ },
+ "@open-rpc/schema-utils-js>ajv": {
+ "globals": {
+ "console": true
+ },
+ "packages": {
+ "@metamask/snaps-utils>fast-json-stable-stringify": true,
+ "@open-rpc/schema-utils-js>ajv>json-schema-traverse": true,
+ "eslint>ajv>uri-js": true,
+ "eslint>fast-deep-equal": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch": {
+ "globals": {
+ "fetch.bind": true
+ },
+ "packages": {
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": {
+ "globals": {
+ "AbortController": true,
+ "Blob": true,
+ "FileReader": true,
+ "FormData": true,
+ "URLSearchParams.prototype.isPrototypeOf": true,
+ "XMLHttpRequest": true,
+ "console.warn": true,
+ "define": true,
+ "setTimeout": true
+ }
+ },
"@popperjs/core": {
"globals": {
"Element": true,
@@ -3835,6 +3886,11 @@
"koa>is-generator-function>has-tostringtag": true
}
},
+ "eslint>ajv>uri-js": {
+ "globals": {
+ "define": true
+ }
+ },
"eth-ens-namehash": {
"globals": {
"name": "write"
@@ -4571,6 +4627,11 @@
"readable-stream": true
}
},
+ "jsonschema": {
+ "packages": {
+ "browserify>url": true
+ }
+ },
"koa>content-disposition>safe-buffer": {
"packages": {
"browserify>buffer": true
diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json
index 2fa339f5201e..e34014875ed1 100644
--- a/lavamoat/browserify/mmi/policy.json
+++ b/lavamoat/browserify/mmi/policy.json
@@ -1551,18 +1551,13 @@
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
"@metamask/eth-sig-util": true,
- "@metamask/message-manager>jsonschema": true,
"@metamask/utils": true,
"browserify>buffer": true,
+ "jsonschema": true,
"uuid": true,
"webpack>events": true
}
},
- "@metamask/message-manager>jsonschema": {
- "packages": {
- "browserify>url": true
- }
- },
"@metamask/message-signing-snap>@noble/ciphers": {
"globals": {
"TextDecoder": true,
@@ -3031,6 +3026,62 @@
"crypto": true
}
},
+ "@open-rpc/schema-utils-js": {
+ "packages": {
+ "@open-rpc/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "@open-rpc/schema-utils-js>ajv": true,
+ "@open-rpc/schema-utils-js>is-url": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true,
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true,
+ "eth-rpc-errors>fast-safe-stringify": true
+ }
+ },
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": {
+ "packages": {
+ "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true,
+ "@open-rpc/test-coverage>isomorphic-fetch": true
+ }
+ },
+ "@open-rpc/schema-utils-js>ajv": {
+ "globals": {
+ "console": true
+ },
+ "packages": {
+ "@metamask/snaps-utils>fast-json-stable-stringify": true,
+ "@open-rpc/schema-utils-js>ajv>json-schema-traverse": true,
+ "eslint>ajv>uri-js": true,
+ "eslint>fast-deep-equal": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch": {
+ "globals": {
+ "fetch.bind": true
+ },
+ "packages": {
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true
+ }
+ },
+ "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": {
+ "globals": {
+ "AbortController": true,
+ "Blob": true,
+ "FileReader": true,
+ "FormData": true,
+ "URLSearchParams.prototype.isPrototypeOf": true,
+ "XMLHttpRequest": true,
+ "console.warn": true,
+ "define": true,
+ "setTimeout": true
+ }
+ },
"@popperjs/core": {
"globals": {
"Element": true,
@@ -3927,6 +3978,11 @@
"koa>is-generator-function>has-tostringtag": true
}
},
+ "eslint>ajv>uri-js": {
+ "globals": {
+ "define": true
+ }
+ },
"eth-ens-namehash": {
"globals": {
"name": "write"
@@ -4663,6 +4719,11 @@
"readable-stream": true
}
},
+ "jsonschema": {
+ "packages": {
+ "browserify>url": true
+ }
+ },
"koa>content-disposition>safe-buffer": {
"packages": {
"browserify>buffer": true
diff --git a/package.json b/package.json
index f37c7a1c4639..491c33b36497 100644
--- a/package.json
+++ b/package.json
@@ -307,7 +307,7 @@
"@metamask/accounts-controller": "^18.2.1",
"@metamask/address-book-controller": "^6.0.0",
"@metamask/announcement-controller": "^7.0.0",
- "@metamask/api-specs": "^0.10.10",
+ "@metamask/api-specs": "^0.10.11",
"@metamask/approval-controller": "^7.0.0",
"@metamask/assets-controllers": "^37.0.0",
"@metamask/base-controller": "^7.0.0",
diff --git a/test/e2e/api-specs/helpers.ts b/test/e2e/api-specs/helpers.ts
index 05ed6a41e977..13312d46a1e9 100644
--- a/test/e2e/api-specs/helpers.ts
+++ b/test/e2e/api-specs/helpers.ts
@@ -3,6 +3,7 @@ import { ErrorObject } from '@open-rpc/meta-schema';
import { JsonRpcResponse } from 'json-rpc-engine';
import { JsonRpcFailure } from '@metamask/utils';
import { Driver } from '../webdriver/driver';
+import { ScopeString } from '../../../app/scripts/lib/multichain-api/scope';
// eslint-disable-next-line @typescript-eslint/no-shadow, @typescript-eslint/no-explicit-any
declare let window: any;
@@ -76,11 +77,95 @@ export const pollForResult = async (
return pollForResult(driver, generatedKey);
};
+export const createCaip27DriverTransport = (
+ driver: Driver,
+ scopeMap: Record,
+) => {
+ // use externally_connectable to communicate with the extension
+ // https://developer.chrome.com/docs/extensions/mv3/messaging/
+ return async (
+ __: string,
+ method: string,
+ params: unknown[] | Record,
+ ) => {
+ const generatedKey = uuid();
+ addToQueue({
+ name: 'transport',
+ resolve: () => {
+ // noop
+ },
+ reject: () => {
+ // noop
+ },
+ task: async () => {
+ // don't wait for executeScript to finish window.ethereum promise
+ // we need this because if we wait for the promise to resolve it
+ // will hang in selenium since it can only do one thing at a time.
+ // the workaround is to put the response on window.asyncResult and poll for it.
+ driver.executeScript(
+ ([m, p, g, s]: [
+ string,
+ unknown[] | Record,
+ string,
+ ScopeString,
+ ]) => {
+ const EXTENSION_ID = 'famgliladofnadeldnodcgnjhafnbnhj';
+ const extensionPort = chrome.runtime.connect(EXTENSION_ID);
+
+ const listener = ({
+ type,
+ data,
+ }: {
+ type: string;
+ data: JsonRpcResponse;
+ }) => {
+ if (type !== 'caip-x') {
+ return;
+ }
+ if (data?.id !== g) {
+ return;
+ }
+
+ if (data.id || (data as JsonRpcFailure).error) {
+ window[g] = data;
+ extensionPort.onMessage.removeListener(listener);
+ }
+ };
+
+ extensionPort.onMessage.addListener(listener);
+ const msg = {
+ type: 'caip-x',
+ data: {
+ jsonrpc: '2.0',
+ method: 'wallet_invokeMethod',
+ params: {
+ request: {
+ method: m,
+ params: p,
+ },
+ scope: s,
+ },
+ id: g,
+ },
+ };
+ extensionPort.postMessage(msg);
+ },
+ method,
+ params,
+ generatedKey,
+ scopeMap[method],
+ );
+ },
+ });
+ return pollForResult(driver, generatedKey);
+ };
+};
+
export const createMultichainDriverTransport = (driver: Driver) => {
// use externally_connectable to communicate with the extension
// https://developer.chrome.com/docs/extensions/mv3/messaging/
return async (
- _: string,
+ __: string,
method: string,
params: unknown[] | Record,
) => {
@@ -151,7 +236,7 @@ export const createMultichainDriverTransport = (driver: Driver) => {
export const createDriverTransport = (driver: Driver) => {
return async (
- _: string,
+ __: string,
method: string,
params: unknown[] | Record,
) => {
diff --git a/test/e2e/api-specs/transform.ts b/test/e2e/api-specs/transform.ts
index 40ec73dfa770..ccbd696d407c 100644
--- a/test/e2e/api-specs/transform.ts
+++ b/test/e2e/api-specs/transform.ts
@@ -9,7 +9,7 @@ const transformOpenRPCDocument = (
openrpcDocument: OpenrpcDocument,
chainId: number,
account: string,
-) => {
+): [OpenrpcDocument, string[], string[]] => {
// transform the document here
const transaction =
@@ -122,6 +122,16 @@ const transformOpenRPCDocument = (
},
];
+ const getProof = openrpcDocument.methods.find(
+ (m) => (m as MethodObject).name === 'eth_getProof',
+ );
+
+ // delete invalid example until its fixed here: https://github.com/ethereum/execution-apis/pull/588
+ (
+ ((getProof as MethodObject).examples?.[0] as ExamplePairingObject)
+ ?.params[1] as ExampleObject
+ ).value.pop();
+
const signTypedData4 = openrpcDocument.methods.find(
(m) => (m as MethodObject).name === 'eth_signTypedData_v4',
);
@@ -284,7 +294,41 @@ const transformOpenRPCDocument = (
// },
// },
];
- return openrpcDocument;
+ // TODO: move these to a "Confirmation" tag in api-specs
+ const methodsWithConfirmations = [
+ 'wallet_requestPermissions',
+ 'eth_requestAccounts',
+ 'wallet_watchAsset',
+ 'personal_sign', // requires permissions for eth_accounts
+ 'wallet_addEthereumChain',
+ 'eth_signTypedData_v4', // requires permissions for eth_accounts
+ 'wallet_switchEthereumChain',
+
+ // commented out because its not returning 4001 error.
+ // see here https://github.com/MetaMask/metamask-extension/issues/24227
+ // 'eth_getEncryptionPublicKey', // requires permissions for eth_accounts
+ ];
+ const filteredMethods = openrpcDocument.methods
+ .filter((_m: unknown) => {
+ const m = _m as MethodObject;
+ return (
+ m.name.includes('snap') ||
+ m.name.includes('Snap') ||
+ m.name.toLowerCase().includes('account') ||
+ m.name.includes('crypt') ||
+ m.name.includes('blob') ||
+ m.name.includes('sendTransaction') ||
+ m.name.startsWith('wallet_scanQRCode') ||
+ methodsWithConfirmations.includes(m.name) ||
+ // filters are currently 0 prefixed for odd length on
+ // extension which doesn't pass spec
+ // see here: https://github.com/MetaMask/eth-json-rpc-filters/issues/152
+ m.name.includes('filter') ||
+ m.name.includes('Filter')
+ );
+ })
+ .map((m) => (m as MethodObject).name);
+ return [openrpcDocument, filteredMethods, methodsWithConfirmations];
};
export default transformOpenRPCDocument;
diff --git a/test/e2e/run-api-specs-multichain.ts b/test/e2e/run-api-specs-multichain.ts
index fb8901acdbc0..f2f049e34504 100644
--- a/test/e2e/run-api-specs-multichain.ts
+++ b/test/e2e/run-api-specs-multichain.ts
@@ -7,9 +7,15 @@ import {
} from '@metamask/api-specs';
import { MethodObject, OpenrpcDocument } from '@open-rpc/meta-schema';
+import JsonSchemaFakerRule from '@open-rpc/test-coverage/build/rules/json-schema-faker-rule';
+import ExamplesRule from '@open-rpc/test-coverage/build/rules/examples-rule';
+import { ScopeString } from '../../app/scripts/lib/multichain-api/scope';
import { Driver, PAGES } from './webdriver/driver';
-import { createMultichainDriverTransport } from './api-specs/helpers';
+import {
+ createCaip27DriverTransport,
+ createMultichainDriverTransport,
+} from './api-specs/helpers';
import FixtureBuilder from './fixture-builder';
import {
@@ -22,6 +28,7 @@ import {
import { MultichainAuthorizationConfirmation } from './api-specs/MultichainAuthorizationConfirmation';
import transformOpenRPCDocument from './api-specs/transform';
import { MultichainAuthorizationConfirmationErrors } from './api-specs/MultichainAuthorizationConfirmationErrors';
+import { ConfirmationsRejectRule } from './api-specs/ConfirmationRejectionRule';
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const mockServer = require('@open-rpc/mock-server/build/index').default;
@@ -44,6 +51,7 @@ async function main() {
// Open Dapp
await openDapp(driver, undefined, DAPP_URL);
+
const doc = await parseOpenRPCDocument(
MultiChainOpenRPCDocument as OpenrpcDocument,
);
@@ -51,6 +59,62 @@ async function main() {
(m) => (m as MethodObject).name === 'wallet_createSession',
);
+ const walletRpcMethods: string[] = [
+ 'wallet_registerOnboarding',
+ 'wallet_scanQRCode',
+ ];
+ const walletEip155Methods = [
+ 'wallet_addEthereumChain',
+ 'personal_sign',
+ 'eth_signTypedData_v4',
+ ];
+
+ const ignoreMethods = [
+ 'wallet_switchEthereumChain',
+ 'wallet_getPermissions',
+ 'wallet_requestPermissions',
+ 'wallet_revokePermissions',
+ 'eth_requestAccounts',
+ 'eth_accounts',
+ 'eth_coinbase',
+ 'net_version',
+ ];
+
+ const transport = createMultichainDriverTransport(driver);
+ const [transformedDoc, filteredMethods, methodsWithConfirmations] =
+ transformOpenRPCDocument(
+ MetaMaskOpenRPCDocument as OpenrpcDocument,
+ chainId,
+ ACCOUNT_1,
+ );
+ const ethereumMethods = transformedDoc.methods
+ .map((m) => (m as MethodObject).name)
+ .filter((m) => {
+ const match =
+ walletRpcMethods.includes(m) ||
+ walletEip155Methods.includes(m) ||
+ ignoreMethods.includes(m);
+ return !match;
+ });
+ const confirmationMethods = methodsWithConfirmations.filter(
+ (m) => !ignoreMethods.includes(m),
+ );
+ const scopeMap: Record = {
+ [`eip155:${chainId}`]: ethereumMethods,
+ 'wallet:eip155': walletEip155Methods,
+ wallet: walletRpcMethods,
+ };
+
+ const reverseScopeMap = Object.entries(scopeMap).reduce(
+ (acc, [scope, methods]: [string, string[]]) => {
+ methods.forEach((method) => {
+ acc[method] = scope;
+ });
+ return acc;
+ },
+ {} as { [method: string]: string },
+ );
+
// fix the example for wallet_createSession
(providerAuthorize as MethodObject).examples = [
{
@@ -61,17 +125,16 @@ async function main() {
name: 'requiredScopes',
value: {
eip155: {
- references: ['1'],
- methods: ['eth_sendTransaction', 'eth_getBalance'],
+ scopes: ['eip155:1337'],
+ methods: ethereumMethods,
+ notifications: ['eth_subscription'],
+ },
+ 'wallet:eip155': {
+ methods: walletEip155Methods,
notifications: [],
},
- },
- },
- {
- name: 'optionalScopes',
- value: {
- 'eip155:1337': {
- methods: ['eth_sendTransaction', 'eth_getBalance'],
+ wallet: {
+ methods: walletRpcMethods,
notifications: [],
},
},
@@ -82,14 +145,19 @@ async function main() {
value: {
sessionId: '0xdeadbeef',
sessionScopes: {
- 'eip155:1': {
- accounts: [`eip155:1:${ACCOUNT_1}`],
- methods: ['eth_sendTransaction', 'eth_getBalance'],
- notifications: [],
- },
[`eip155:${chainId}`]: {
accounts: [`eip155:${chainId}:${ACCOUNT_1}`],
- methods: ['eth_sendTransaction', 'eth_getBalance'],
+ methods: ethereumMethods,
+ notifications: ['eth_subscription'],
+ },
+ 'wallet:eip155': {
+ accounts: [`wallet:eip155:${ACCOUNT_1}`],
+ methods: walletEip155Methods,
+ notifications: [],
+ },
+ wallet: {
+ accounts: [],
+ methods: walletRpcMethods,
notifications: [],
},
},
@@ -98,13 +166,6 @@ async function main() {
},
];
- const transport = createMultichainDriverTransport(driver);
- const transformedDoc = transformOpenRPCDocument(
- MetaMaskOpenRPCDocument as OpenrpcDocument,
- chainId,
- ACCOUNT_1,
- );
-
const server = mockServer(port, transformedDoc);
server.start();
@@ -131,10 +192,54 @@ async function main() {
],
});
+ const testCoverageResultsCaip27 = await testCoverage({
+ openrpcDocument: MetaMaskOpenRPCDocument as OpenrpcDocument,
+ transport: createCaip27DriverTransport(driver, reverseScopeMap),
+ reporters: [
+ 'console-streaming',
+ new HtmlReporter({
+ autoOpen: !process.env.CI,
+ destination: `${process.cwd()}/html-report-caip27`,
+ }),
+ ],
+ skip: [
+ 'eth_coinbase',
+ 'wallet_revokePermissions',
+ 'wallet_requestPermissions',
+ 'wallet_getPermissions',
+ 'eth_accounts',
+ 'eth_requestAccounts',
+ 'net_version', // not in the spec yet for some reason
+ // these 2 methods below are not supported by MetaMask extension yet and
+ // don't get passed through. See here: https://github.com/MetaMask/metamask-extension/issues/24225
+ 'eth_getBlockReceipts',
+ 'eth_maxPriorityFeePerGas',
+ ],
+ rules: [
+ new JsonSchemaFakerRule({
+ only: [],
+ skip: filteredMethods,
+ numCalls: 2,
+ }),
+ new ExamplesRule({
+ only: [],
+ skip: filteredMethods,
+ }),
+ new ConfirmationsRejectRule({
+ driver,
+ only: confirmationMethods,
+ }),
+ ],
+ });
+
+ const joinedResults = testCoverageResults.concat(
+ testCoverageResultsCaip27,
+ );
+
await driver.quit();
// if any of the tests failed, exit with a non-zero code
- if (testCoverageResults.every((r) => r.valid)) {
+ if (joinedResults.every((r) => r.valid)) {
process.exit(0);
} else {
process.exit(1);
diff --git a/test/e2e/run-openrpc-api-test-coverage.ts b/test/e2e/run-openrpc-api-test-coverage.ts
index 0078f0ba1424..048699ee8e94 100644
--- a/test/e2e/run-openrpc-api-test-coverage.ts
+++ b/test/e2e/run-openrpc-api-test-coverage.ts
@@ -4,7 +4,7 @@ import HtmlReporter from '@open-rpc/test-coverage/build/reporters/html-reporter'
import ExamplesRule from '@open-rpc/test-coverage/build/rules/examples-rule';
import JsonSchemaFakerRule from '@open-rpc/test-coverage/build/rules/json-schema-faker-rule';
-import { MethodObject, OpenrpcDocument } from '@open-rpc/meta-schema';
+import { OpenrpcDocument } from '@open-rpc/meta-schema';
import { MetaMaskOpenRPCDocument } from '@metamask/api-specs';
import { ConfirmationsRejectRule } from './api-specs/ConfirmationRejectionRule';
@@ -45,50 +45,16 @@ async function main() {
await openDapp(driver, undefined, DAPP_URL);
const transport = createDriverTransport(driver);
- const doc: OpenrpcDocument = transformOpenRPCDocument(
- MetaMaskOpenRPCDocument as unknown as OpenrpcDocument,
- chainId,
- ACCOUNT_1,
- );
+ const [doc, filteredMethods, methodsWithConfirmations] =
+ transformOpenRPCDocument(
+ MetaMaskOpenRPCDocument as unknown as OpenrpcDocument,
+ chainId,
+ ACCOUNT_1,
+ );
const server = mockServer(port, doc);
server.start();
- // TODO: move these to a "Confirmation" tag in api-specs
- const methodsWithConfirmations = [
- 'wallet_requestPermissions',
- 'eth_requestAccounts',
- 'wallet_watchAsset',
- 'personal_sign', // requires permissions for eth_accounts
- 'wallet_addEthereumChain',
- 'eth_signTypedData_v4', // requires permissions for eth_accounts
- 'wallet_switchEthereumChain',
-
- // commented out because its not returning 4001 error.
- // see here https://github.com/MetaMask/metamask-extension/issues/24227
- // 'eth_getEncryptionPublicKey', // requires permissions for eth_accounts
- ];
- const filteredMethods = doc.methods
- .filter((_m: unknown) => {
- const m = _m as MethodObject;
- return (
- m.name.includes('snap') ||
- m.name.includes('Snap') ||
- m.name.toLowerCase().includes('account') ||
- m.name.includes('crypt') ||
- m.name.includes('blob') ||
- m.name.includes('sendTransaction') ||
- m.name.startsWith('wallet_scanQRCode') ||
- methodsWithConfirmations.includes(m.name) ||
- // filters are currently 0 prefixed for odd length on
- // extension which doesn't pass spec
- // see here: https://github.com/MetaMask/eth-json-rpc-filters/issues/152
- m.name.includes('filter') ||
- m.name.includes('Filter')
- );
- })
- .map((m) => (m as MethodObject).name);
-
const testCoverageResults = await testCoverage({
openrpcDocument: await parseOpenRPCDocument(doc),
transport,
diff --git a/yarn.lock b/yarn.lock
index 57484eb73412..4d4921fbffda 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4867,10 +4867,10 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/api-specs@npm:^0.10.10":
- version: 0.10.10
- resolution: "@metamask/api-specs@npm:0.10.10"
- checksum: 10/0318b5b5e1fc39e3d0b7c9c44abd3b459bd15e7e8578c062d059806c12836975ef0a69fa090022eb87a372d766105b0bec222c13507d95eaea9f5b38dcfc7313
+"@metamask/api-specs@npm:^0.10.11":
+ version: 0.10.11
+ resolution: "@metamask/api-specs@npm:0.10.11"
+ checksum: 10/d1873843d9393008a9acc3c70dfadb12d04edc33299acfeb7cd68f15fdd760d8004e5c90868bec578344659308af24ad0ee7793941cb0a9c7c6546f8ef3105a5
languageName: node
linkType: hard
@@ -26128,7 +26128,7 @@ __metadata:
"@metamask/accounts-controller": "npm:^18.2.1"
"@metamask/address-book-controller": "npm:^6.0.0"
"@metamask/announcement-controller": "npm:^7.0.0"
- "@metamask/api-specs": "npm:^0.10.10"
+ "@metamask/api-specs": "npm:^0.10.11"
"@metamask/approval-controller": "npm:^7.0.0"
"@metamask/assets-controllers": "npm:^37.0.0"
"@metamask/auto-changelog": "npm:^2.1.0"