diff --git a/SIPS/sip-26.md b/SIPS/sip-26.md index b6d3b68..7179a6f 100644 --- a/SIPS/sip-26.md +++ b/SIPS/sip-26.md @@ -53,27 +53,36 @@ Router integrates with the RPC Router, Account Snaps, and Protocol Snaps. `resolveAccountAddress` method to extract the account address from the request object. + ### RPC Router -The RPC Router is a native wallet component responsible for routing RPC requests made via the Multichain API to either a Protocol Snap or the Account Router depending on whether the requested method is registered as a signing method or not. +The RPC Router is a native wallet component responsible for routing RPC requests made via the Multichain API to either a Protocol Snap or the Account Router, depending on whether the requested method is registered as a signing method or not. #### Role and Functioning -- **Initial Request Handling**: Receives all incoming RPC requests made via the Multichain API that do not have an `eip155` namespace - for now `eip155` scoped requests will continue to be handled by the native wallet, though we will need to consider how to allow users to decide whether to route these requests to snaps as well in the future. +- **Initial Request Handling**: Receives all incoming RPC requests made via the Multichain API that do not have an `eip155` namespace. For now, `eip155` scoped requests will continue to be handled by the native wallet, though we will need to consider how to allow users to decide whether to route these requests to snaps as well in the future. -- **Method Registration**: Provides entry points for the Account Router and Protocol Snaps to register their methods and scopes and maintains a registry of these RPC methods and their respective handlers. +- **Method Registration**: Provides entry points for the Account Router and Protocol Snaps to register their methods, method signatures, and scopes, and maintains a registry of these RPC methods and their respective handlers. -- **Request Routing**: Uses the internal registry to determine the correct handler for each incoming RPC request based on the method name and scope strings, forwarding the request to either the Account Router or the appropriate Protocol Snap. +- **Request Routing**: Uses the internal registry to determine the correct handler for each incoming RPC request based on the method name, method signatures, and scope strings, forwarding the request to either the Account Router or the appropriate Protocol Snap. #### Interface -The RPC Router provides a method for registering RPC methods with their scopes and handler. Both the Account Router and Protocol Snaps use this method to inform the RPC Router about the methods they can handle. The origin of the `rpcRouter_registerMethods` call, either a specific protocol snap (e.g. `npm:@solana/example-protocol-snap`) or the Account Router (which will use its owner internal registry to forward to the appropriate Account Snaps) - is used to determine which handler the method will be registered with. +The RPC Router provides a method for registering RPC methods with their scopes and handler. Both the Account Router and Protocol Snaps use this method to inform the RPC Router about the methods they can handle. The origin of the `rpcRouter_registerMethods` call, either a specific protocol snap (e.g., `npm:@solana/example-protocol-snap`) or the Account Router (which will use its own internal registry to forward to the appropriate Account Snaps), is used to determine which handler the method will be registered with. + +##### Method Signature Matching + +To ensure that incoming requests are handled correctly, methods are registered along with their method signatures using the OpenRPC schema notation. When a request is received, the RPC Router compares the request's parameters against the registered method signatures to find a matching handler. This ensures that the request parameters are valid and that the correct handler processes the request. + +Multiple handlers can register for the same method with the same or different method signatures. In such cases, the RPC Router will select the appropriate handler based on the method signature that matches the request's parameters. This allows for flexibility in handling methods that may have overloaded signatures or that can be processed by different handlers depending on the parameter types. ##### Specification ```typescript +import { MethodObject } from "@open-rpc/meta-schema"; + function rpcRouter_registerMethods( - methodNames: string[], + methods: MethodObject[], scopeStrings: ScopeString[] ): void; ``` @@ -81,134 +90,310 @@ function rpcRouter_registerMethods( ##### Example Usage ```typescript -// Account Router registering methods for Solana -rpcRouter_registerMethods(["signTransaction", "signMessage"], ["solana"]); -// A Protocol Snap registering non-signing methods for Solana +import { MethodObject } from "@open-rpc/meta-schema"; + +// Account Router registering methods for Solana with OpenRPC method definitions rpcRouter_registerMethods( - ["getAccountInfo", "getBalance", "getRecentBlockhash"], + [ + { + name: "signTransaction", + params: [ + { + name: "transaction", + required: true, + schema: { type: "string", format: "base64" }, + }, + ], + result: { + name: "signedTransaction", + schema: { type: "string", format: "base64" }, + }, + }, + { + name: "signMessage", + params: [ + { + name: "message", + required: true, + schema: { type: "string" }, + }, + ], + result: { + name: "signedMessage", + schema: { type: "string", format: "base64" }, + }, + }, + ], + ["solana"] +); + +// A Protocol Snap registering non-signing methods for Solana with OpenRPC method definitions +rpcRouter_registerMethods( + [ + { + name: "getAccountInfo", + params: [ + { + name: "publicKey", + required: true, + schema: { type: "string", format: "publicKey" }, + }, + ], + result: { + name: "accountInfo", + schema: { type: "object" }, + }, + }, + { + name: "getBalance", + params: [ + { + name: "publicKey", + required: true, + schema: { type: "string", format: "publicKey" }, + }, + ], + result: { + name: "balance", + schema: { type: "number" }, + }, + }, + { + name: "getRecentBlockhash", + params: [], + result: { + name: "blockhash", + schema: { type: "string" }, + }, + }, + ], ["solana"] ); ``` ##### Example Registry -The following is an example of a registry that includes methods registered by both the Account Router and Protocol Snaps for different chains and namespaces. +The following is an example of a registry that includes methods registered by both the Account Router and Protocol Snaps for different chains and namespaces, including their method signatures. ```typescript +import { MethodObject } from "@open-rpc/meta-schema"; + +type RpcMethodRegistry = { + [scope: string]: { + [methodName: string]: Array<{ + methodSignature: MethodObject; + handlerIds: string[]; + }>; + }; +}; + const rpcMethodRegistry: RpcMethodRegistry = { solana: { - signTransaction: { - handlerIds: ["AccountRouter"], - }, - signMessage: { - handlerIds: ["AccountRouter"], - }, - getAccountInfo: { - handlerIds: [ - "npm:@example/solana-protocol-snap", - "npm:@yet-another/solana-protocol-snap", - ], - }, - getBalance: { - handlerIds: ["npm:@example/solana-protocol-snap"], - }, - getTransaction: { - handlerIds: ["npm:@another/solana-protocol-snap"], - }, + signTransaction: [ + { + methodSignature: { + name: "signTransaction", + params: [ + { + name: "transaction", + required: true, + schema: { type: "string", format: "base64" }, + }, + ], + result: { + name: "signedTransaction", + schema: { type: "string", format: "base64" }, + }, + }, + handlerIds: ["AccountRouter"], + }, + // Other implementations of solana:signTransaction can be added here + ], + signMessage: [ + { + methodSignature: { + name: "signMessage", + params: [ + { + name: "message", + required: true, + schema: { type: "string" }, + }, + ], + result: { + name: "signedMessage", + schema: { type: "string", format: "base64" }, + }, + }, + handlerIds: ["AccountRouter"], + }, + ], + getAccountInfo: [ + { + methodSignature: { + name: "getAccountInfo", + params: [ + { + name: "publicKey", + required: true, + schema: { type: "string", format: "publicKey" }, + }, + ], + result: { + name: "accountInfo", + schema: { type: "object" }, + }, + }, + handlerIds: ["npm:@example/solana-protocol-snap"], + }, + { + methodSignature: { + name: "getAccountInfo", + params: [ + { + name: "accountId", + required: true, + schema: { type: "string" }, + }, + ], + result: { + name: "accountInfo", + schema: { type: "object" }, + }, + }, + handlerIds: ["npm:@yet-another/solana-protocol-snap"], + }, + ], // ... more methods }, "bip122:000000000019d6689c085ae165831e93": { // Bitcoin Mainnet - sendtoaddress: { - handlerIds: ["AccountRouter"], - }, - signrawtransactionwithwallet: { - handlerIds: ["AccountRouter"], - }, - getnewaddress: { - handlerIds: ["AccountRouter"], - }, - getblockchaininfo: { - handlerIds: [ - "npm:@example/bitcoin-protocol-snap", - "npm:@another/bitcoin-protocol-snap", - ], - }, - getblock: { - handlerIds: [ - "npm:@example/bitcoin-protocol-snap", - "npm:@another/bitcoin-protocol-snap", - ], - }, + sendtoaddress: [ + { + methodSignature: { + name: "sendtoaddress", + params: [ + { + name: "address", + required: true, + schema: { type: "string" }, + }, + { + name: "amount", + required: true, + schema: { type: "number" }, + }, + ], + result: { + name: "transactionId", + schema: { type: "string" }, + }, + }, + handlerIds: ["AccountRouter"], + }, + // Other implementations of sendtoaddress can be added here + ], + getblockchaininfo: [ + { + methodSignature: { + name: "getblockchaininfo", + params: [], + result: { + name: "info", + schema: { type: "object" }, + }, + }, + handlerIds: ["npm:@example/bitcoin-protocol-snap"], + }, + { + methodSignature: { + name: "getblockchaininfo", + params: [ + { + name: "verbosity", + required: false, + schema: { type: "number" }, + }, + ], + result: { + name: "info", + schema: { type: "object" }, + }, + }, + handlerIds: ["npm:@another/bitcoin-protocol-snap"], + }, + ], // ... more methods }, cosmos: { - cosmos_signTransaction: { - handlerIds: ["AccountRouter"], - }, - cosmos_signMessage: { - handlerIds: ["AccountRouter"], - }, - abci_query: { - handlerIds: ["npm:@cosmos/general-protocol-snap"], - }, - block: { - handlerIds: ["npm:@cosmos/general-protocol-snap"], - }, - tx: { - handlerIds: ["npm:@cosmos/general-protocol-snap"], - }, - // ... methods general to the Cosmos namespace - }, - "cosmos:cosmoshub-4": { - // Cosmos Hub - block: { - handlerIds: ["npm:@cosmoshub/protocol-snap"], - }, - getValidators: { - handlerIds: ["npm:@cosmoshub/protocol-snap"], - }, - // ... more methods specific to Cosmos Hub - }, - "cosmos:osmosis-1": { - // Osmosis - block: { - handlerIds: ["npm:@osmosis/protocol-snap"], - }, - getPools: { - handlerIds: ["npm:@osmosis/protocol-snap"], - }, - // ... more methods specific to Osmosis + cosmos_signTransaction: [ + { + methodSignature: { + name: "cosmos_signTransaction", + params: [ + { + name: "signDoc", + required: true, + schema: { type: "object" }, + }, + ], + result: { + name: "signedTransaction", + schema: { type: "string" }, + }, + }, + handlerIds: ["AccountRouter"], + }, + ], + abci_query: [ + { + methodSignature: { + name: "abci_query", + params: [ + { name: "path", required: true, schema: { type: "string" } }, + { name: "data", required: true, schema: { type: "string" } }, + { name: "height", required: false, schema: { type: "number" } }, + { name: "prove", required: false, schema: { type: "boolean" } }, + ], + result: { + name: "response", + schema: { type: "object" }, + }, + }, + handlerIds: ["npm:@cosmos/general-protocol-snap"], + }, + ], + // ... more methods }, + // ... more chains and methods }; ``` ##### Registration Process -Protocol Snaps register their methods and scopes during initialization (obtained from their manifest file) or when their capabilities change. Account Snaps register their signing methods with the Account Router, which then registers them with the RPC Router. The RPC Router maintains an internal registry mapping methods and scopes to their respective handlers. +Protocol Snaps register their methods, method signatures, and scopes during initialization (obtained from their manifest file) or when their capabilities change. Account Snaps register their signing methods with the Account Router, which then registers them with the RPC Router. The RPC Router maintains an internal registry mapping methods, method signatures, and scopes to their respective handlers. #### Request Handling Flow -When the MetaMask wallet receives a [CAIP-27][caip-27] request via the Multichain API, the RPC Router processes it through the following steps: +When the wallet receives a [CAIP-27][caip-27] request via the Multichain API, the RPC Router processes it through the following steps: -1. **Extract Request Details**: Parses the request to identify the method name and scope (chain or namespace). The scope is determined using [CAIP-2][caip-2] identifiers. +1. **Extract Request Details**: Parses the request to identify the method name, parameters, and scope (chain or namespace). The scope is determined using [CAIP-2][caip-2] identifiers. -2. **Check Namespace**: Determines if the request has an `eip155` namespace (Ethereum-based chains). If it does, the request is handled natively by the wallet, otherwise it is forwarded to the RPC Router. +2. **Check Namespace**: Determines if the request has an `eip155` namespace (Ethereum-based chains). If it does, the request is handled natively by the wallet; otherwise, it is forwarded to the RPC Router. -3. **Lookup Handler**: If the request does not have an `eip155` namespace, the RPC Router checks its registry to find a handler for the method and scope. +3. **Lookup Handler**: If the request does not have an `eip155` namespace, the RPC Router checks its registry to find handlers for the method and scope. It then matches the incoming request's parameters against the registered method signatures. 4. **Determine Handler Type**: - - **Protocol Snap**: If the method is registered with a Protocol Snap, the request is forwarded to it. - - **Account Router**: If the method is registered with the Account Router, the request is forwarded to it. - - **Unregistered Method**: If the method is not registered, an error is returned to the requester. + - **Protocol Snap**: If the method is registered with a Protocol Snap and the method signature matches the request parameters, the request is forwarded to it. + - **Account Router**: If the method is registered with the Account Router and the method signature matches the request parameters, the request is forwarded to it. + - **Unregistered Method**: If the method is not registered or no method signatures match the request parameters, an error is returned to the requester. 5. **Forward Request**: The RPC Router forwards the request to the identified handler. 6. **Return Response**: The handler processes the request and returns a response, which the RPC Router then returns back to the Multichain API RPC pipeline to be returned to the requester. ---- ### Account Router