@metamask/snaps-rpc-methods
Version:
MetaMask Snaps JSON-RPC method implementations
1 lines • 9.96 kB
Source Map (JSON)
{"version":3,"file":"experimentalProviderRequest.mjs","sourceRoot":"","sources":["../../src/permitted/experimentalProviderRequest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';\nimport type { PermittedHandlerExport } from '@metamask/permission-controller';\nimport { rpcErrors } from '@metamask/rpc-errors';\nimport type {\n JsonRpcRequest,\n ProviderRequestParams,\n ProviderRequestResult,\n} from '@metamask/snaps-sdk';\nimport { type InferMatching } from '@metamask/snaps-utils';\nimport {\n StructError,\n create,\n object,\n optional,\n string,\n type,\n} from '@metamask/superstruct';\nimport {\n bigIntToHex,\n parseCaipChainId,\n type PendingJsonRpcResponse,\n type Json,\n CaipChainIdStruct,\n JsonRpcParamsStruct,\n} from '@metamask/utils';\n\nimport { SnapEndowments } from '../endowments';\nimport type { MethodHooksObject } from '../utils';\n\n// Read-only methods that are currently allowed for this RPC method.\nconst METHOD_ALLOWLIST = Object.freeze([\n 'eth_blockNumber',\n 'eth_call',\n 'eth_chainId',\n 'eth_coinbase',\n 'eth_estimateGas',\n 'eth_feeHistory',\n 'eth_gasPrice',\n 'eth_getBalance',\n 'eth_getBlockByHash',\n 'eth_getBlockByNumber',\n 'eth_getBlockTransactionCountByHash',\n 'eth_getBlockTransactionCountByNumber',\n 'eth_getCode',\n 'eth_getFilterChanges',\n 'eth_getFilterLogs',\n 'eth_getLogs',\n 'eth_getProof',\n 'eth_getStorageAt',\n 'eth_getTransactionByBlockHashAndIndex',\n 'eth_getTransactionByBlockNumberAndIndex',\n 'eth_getTransactionByHash',\n 'eth_getTransactionCount',\n 'eth_getTransactionReceipt',\n 'eth_getUncleByBlockHashAndIndex',\n 'eth_getUncleByBlockNumberAndIndex',\n 'eth_getUncleCountByBlockHash',\n 'eth_getUncleCountByBlockNumber',\n 'eth_getWork',\n 'eth_hashrate',\n 'eth_mining',\n 'eth_newBlockFilter',\n 'eth_newFilter',\n 'eth_newPendingTransactionFilter',\n 'eth_protocolVersion',\n 'eth_sendRawTransaction',\n 'eth_submitHashrate',\n 'eth_submitWork',\n 'eth_syncing',\n 'eth_uninstallFilter',\n 'net_listening',\n 'net_peerCount',\n 'net_version',\n 'web3_clientVersion',\n 'web3_sha3',\n]);\n\nconst hookNames: MethodHooksObject<ProviderRequestMethodHooks> = {\n hasPermission: true,\n getNetworkConfigurationByChainId: true,\n getNetworkClientById: true,\n};\n\ntype NetworkConfiguration = {\n defaultRpcEndpointIndex: number;\n rpcEndpoints: { networkClientId: string }[];\n};\n\nexport type ProviderRequestMethodHooks = {\n hasPermission: (permissionName: string) => boolean;\n\n getNetworkConfigurationByChainId: (\n chainId: string,\n ) => NetworkConfiguration | undefined;\n\n getNetworkClientById: (id: string) => {\n provider: { request: (request: Json) => Promise<Json> };\n };\n};\n\nexport const providerRequestHandler: PermittedHandlerExport<\n ProviderRequestMethodHooks,\n ProviderRequestParameters,\n ProviderRequestResult\n> = {\n methodNames: ['snap_experimentalProviderRequest'],\n implementation: providerRequestImplementation,\n hookNames,\n};\n\nconst ProviderRequestParametersStruct = object({\n chainId: CaipChainIdStruct,\n request: type({\n method: string(),\n params: optional(JsonRpcParamsStruct),\n }),\n});\n\nexport type ProviderRequestParameters = InferMatching<\n typeof ProviderRequestParametersStruct,\n ProviderRequestParams\n>;\n\n/**\n * The `snap_experimentalProviderRequest` method implementation.\n *\n * This RPC method lets Snaps make requests to MetaMask networks that are not currently selected in the UI.\n *\n * The RPC method requires the caller to have the endowment:ethereum-provider permission.\n *\n * NOTE: This implementation is experimental and may be removed or changed without warning.\n *\n * @param req - The JSON-RPC request object.\n * @param res - The JSON-RPC response object.\n * @param _next - The `json-rpc-engine` \"next\" callback. Not used by this\n * function.\n * @param end - The `json-rpc-engine` \"end\" callback.\n * @param hooks - The RPC method hooks.\n * @param hooks.hasPermission - Checks whether a given origin has a given permission.\n * @param hooks.getNetworkConfigurationByChainId - Get a network configuration for a given chain ID.\n * @param hooks.getNetworkClientById - Get a network client for a given ID.\n * @returns Nothing.\n */\nasync function providerRequestImplementation(\n req: JsonRpcRequest<ProviderRequestParams>,\n // `ProviderRequestResult` is an alias for `Json` (which is the default type\n // argument for `PendingJsonRpcResponse`), but that may not be the case in the\n // future. We use `ProviderRequestResult` here to make it clear that this is\n // the expected type of the result.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments\n res: PendingJsonRpcResponse<ProviderRequestResult>,\n _next: unknown,\n end: JsonRpcEngineEndCallback,\n {\n hasPermission,\n getNetworkConfigurationByChainId,\n getNetworkClientById,\n }: ProviderRequestMethodHooks,\n): Promise<void> {\n if (!hasPermission(SnapEndowments.EthereumProvider)) {\n return end(rpcErrors.methodNotFound());\n }\n\n const { params } = req;\n\n try {\n const { chainId, request } = getValidatedParams(params);\n\n if (!METHOD_ALLOWLIST.includes(request.method)) {\n return end(rpcErrors.methodNotFound());\n }\n\n const parsedChainId = parseCaipChainId(chainId);\n\n if (parsedChainId.namespace !== 'eip155') {\n return end(\n rpcErrors.invalidParams({\n message: 'Only EVM networks are currently supported.',\n }),\n );\n }\n\n const numericalChainId = BigInt(parsedChainId.reference);\n\n const networkConfiguration = getNetworkConfigurationByChainId(\n bigIntToHex(numericalChainId),\n );\n\n if (!networkConfiguration) {\n return end(\n rpcErrors.invalidParams({\n message: 'The requested network is not available.',\n }),\n );\n }\n\n const rpc =\n networkConfiguration.rpcEndpoints[\n networkConfiguration.defaultRpcEndpointIndex\n ];\n\n const networkClient = getNetworkClientById(rpc.networkClientId);\n\n const { provider } = networkClient;\n\n res.result = await provider.request(request);\n } catch (error) {\n return end(error);\n }\n\n return end();\n}\n\n/**\n * Validate the method `params` and returns them cast to the correct\n * type. Throws if validation fails.\n *\n * @param params - The unvalidated params object from the method request.\n * @returns The validated updateInterface method parameter object.\n */\nfunction getValidatedParams(params: unknown): ProviderRequestParameters {\n try {\n return create(params, ProviderRequestParametersStruct);\n } catch (error) {\n if (error instanceof StructError) {\n throw rpcErrors.invalidParams({\n message: `Invalid params: ${error.message}.`,\n });\n }\n /* istanbul ignore next */\n throw rpcErrors.internal();\n }\n}\n"]}