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"