|
| 1 | +--- |
| 2 | +sip: 26 |
| 3 | +title: Non-EVM protocol support |
| 4 | +status: Draft |
| 5 | +author: Daniel Rocha (@danroc), Frederik Bolding (@FrederikBolding), Alex Donesky (@adonesky1) |
| 6 | +created: 2024-08-28 |
| 7 | +--- |
| 8 | + |
| 9 | +## Abstract |
| 10 | + |
| 11 | +This SIP presents an architecture to enable Snaps to expose blockchain-specific |
| 12 | +methods to dapps, extending MetaMask's functionality to support a multichain |
| 13 | +ecosystem. |
| 14 | + |
| 15 | +## Motivation |
| 16 | + |
| 17 | +Currently, MetaMask is limited to EVM-compatible networks. This proposal aims |
| 18 | +to empower developers, both first- and third-party, to use Snaps to add native |
| 19 | +support for non-EVM-compatible chains within MetaMask. |
| 20 | + |
| 21 | +## Specification |
| 22 | + |
| 23 | +> Formal specifications are written in TypeScript. |
| 24 | +
|
| 25 | +### Language |
| 26 | + |
| 27 | +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", |
| 28 | +"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" written |
| 29 | +in uppercase in this document are to be interpreted as described in [RFC |
| 30 | +2119](https://www.ietf.org/rfc/rfc2119.txt). |
| 31 | + |
| 32 | +### High-Level architecture |
| 33 | + |
| 34 | +The diagram below represents a high-level architecture of how the RPC Router |
| 35 | +integrates inside MetaMask to allow Snaps to expose protocol methods to dapps |
| 36 | +and the MetaMask clients. |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +- **Account Snaps**: Snaps that implement the [Keyring API][keyring-api] and are responsible |
| 41 | + for signing requests and managing accounts. |
| 42 | + |
| 43 | +- **Protocol Snaps**: Snaps that implement protocol methods that do not require |
| 44 | + an account to be executed. |
| 45 | + |
| 46 | +- **RPC Router**: Native component that forwards RPC requests to the |
| 47 | + appropriate Protocol Snap or Account Snap. |
| 48 | + |
| 49 | +- **Keyring Controller**: Native component responsible for forwarding signing |
| 50 | + requests to the appropriate keyring implementation. |
| 51 | + |
| 52 | +- **Accounts Controller**: Native component responsible for managing accounts |
| 53 | + inside MetaMask. It stores all non-sensitive account information. |
| 54 | + |
| 55 | +- **Snaps Keyring**: Native component that acts as a bridge between the |
| 56 | + Keyring Controller and the Account Snaps. |
| 57 | + |
| 58 | +### Components |
| 59 | + |
| 60 | +Here is a brief description of the components involved in this architecture |
| 61 | +which will require to be implemented or modified. |
| 62 | + |
| 63 | +#### RPC Router |
| 64 | + |
| 65 | +The RPC Router will be a new native component responsible for routing JSON-RPC |
| 66 | +requests to the appropriate Snap or keyring. |
| 67 | + |
| 68 | +To route a request, the RPC Router MUST extract the method name and [CAIP-2 chainId][caip-2] |
| 69 | +from the request object. It then determines whether the method is supported by |
| 70 | +a Protocol Snap or an Account Snap, with Account Snaps taking precedence over |
| 71 | +Protocol Snaps. |
| 72 | + |
| 73 | +If the method is supported by an Account Snap, the RPC Router forwards the |
| 74 | +request to the Keyring Controller; otherwise, it forwards the request to the |
| 75 | +appropriate Protocol Snap. |
| 76 | + |
| 77 | +#### Snaps Keyring |
| 78 | + |
| 79 | +The Snaps Keyring is an existing native component that exposes the |
| 80 | +[snap_manageAccounts][snap-manage-accs] method, allowing Account Snaps to |
| 81 | +register, remove, and update accounts. |
| 82 | + |
| 83 | +For example, this code can be used by an Account Snap to register a new account |
| 84 | +with the Account Router: |
| 85 | + |
| 86 | +```typescript |
| 87 | +// This will notify the Account Router that a new account was created, and the |
| 88 | +// Account Router will register this account as available for signing requests |
| 89 | +// using the `eth_signTypedData_v4` method. |
| 90 | +await snap.request({ |
| 91 | + method: "snap_manageAccounts", |
| 92 | + params: { |
| 93 | + method: "notify:accountCreated", |
| 94 | + params: { |
| 95 | + account: { |
| 96 | + id: "74bb3393-f267-48ee-855a-2ba575291ab0", |
| 97 | + type: "eip155:eoa", |
| 98 | + address: "0x1234567890123456789012345678901234567890", |
| 99 | + methods: ["eth_signTypedData_v4"], |
| 100 | + options: {}, |
| 101 | + }, |
| 102 | + }, |
| 103 | + }, |
| 104 | +}); |
| 105 | +``` |
| 106 | + |
| 107 | +Similar events are available to notify about the removal and update of |
| 108 | +accounts: `notify:accountRemoved` and `notify:accountUpdated`. |
| 109 | + |
| 110 | +Additionally, the Snaps Keyring expects the Account Snap to implement the |
| 111 | +Keyring API so it can forward signing requests to it through the |
| 112 | +[`keyring_submitRequest`][submit-request] method. |
| 113 | + |
| 114 | +#### Account Snaps |
| 115 | + |
| 116 | +As part of the Keyring API, non-EVM Account Snaps MUST also implement support |
| 117 | +for the `keyring_resolveAccountAddress` RPC method defined below. It is used |
| 118 | +by the RPC Router to extract the address of the account that should handle |
| 119 | +the signing request from the request object. |
| 120 | + |
| 121 | +```typescript |
| 122 | +type ResolveAccountAddressRequest = { |
| 123 | + method: "keyring_resolveAccountAddress"; |
| 124 | + params: { |
| 125 | + scope: CaipChainId; |
| 126 | + request: JsonRpcRequest; |
| 127 | + }; |
| 128 | +}; |
| 129 | +``` |
| 130 | +`scope` - The [CAIP-2][caip-2] chainId the request is targeting |
| 131 | + |
| 132 | +`request` - A `JsonRpcRequest` containing strictly JSON-serializable values. |
| 133 | + |
| 134 | +The implementation MUST return a value of the type `{ address: string }` or `null`. |
| 135 | + |
| 136 | +#### Protocol Snaps |
| 137 | + |
| 138 | +Protocol Snaps implement and expose methods that do not require an account to |
| 139 | +execute and MUST list their supported methods and notifications in their manifest file: |
| 140 | + |
| 141 | +```json5 |
| 142 | +"initialPermissions": { |
| 143 | + "endowment:protocol": { |
| 144 | + "scopes": { |
| 145 | + "<caip2_chainId>": { |
| 146 | + "methods": [ |
| 147 | + // List of supported methods |
| 148 | + ], |
| 149 | + "notifications": [ |
| 150 | + // List of supported notifications |
| 151 | + ] |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | +``` |
| 157 | + |
| 158 | +Additionally protocol Snaps MUST implement the `onProtocolRequest` handler: |
| 159 | + |
| 160 | +```typescript |
| 161 | +import { OnProtocolRequestHandler } from "@metamask/snap-sdk"; |
| 162 | + |
| 163 | +export const onProtocolRequest: OnProtocolRequestHandler = async ({ |
| 164 | + origin, |
| 165 | + scope, |
| 166 | + request, |
| 167 | +}) => { |
| 168 | + // Return protocol responses |
| 169 | +}; |
| 170 | +``` |
| 171 | + |
| 172 | +The interface for an `onProtocolRequest` handler function’s arguments is: |
| 173 | + |
| 174 | +```typescript |
| 175 | +interface OnProtocolRequestArguments { |
| 176 | + origin: string; |
| 177 | + scope: CaipChainId; |
| 178 | + request: JsonRpcRequest; |
| 179 | +} |
| 180 | +``` |
| 181 | + |
| 182 | +`origin` - The origin making the protocol request (i.e. a dapp). |
| 183 | + |
| 184 | +`scope` - The [CAIP-2][caip-2] chainId the request is targeting. |
| 185 | + |
| 186 | +`request` - A `JsonRpcRequest` containing strictly JSON-serializable values. |
| 187 | + |
| 188 | +Any JSON-serializable value is allowed as the return value for `onProtocolRequest`. |
| 189 | + |
| 190 | +## Copyright |
| 191 | + |
| 192 | +Copyright and related rights waived via [CC0](../LICENSE). |
| 193 | + |
| 194 | +[keyring-api]: https://github.com/MetaMask/accounts/tree/main/packages/keyring-api |
| 195 | +[snap-manage-accs]: https://docs.metamask.io/snaps/reference/snaps-api/#snap_manageaccounts |
| 196 | +[submit-request]: https://docs.metamask.io/snaps/reference/keyring-api/account-management/#keyring_submitrequest |
| 197 | +[caip-2]: https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md |
0 commit comments