SIP-26: Non-EVM protocol support Source

Author Daniel Rocha, Frederik Bolding, Alex Donesky
Status Draft
Created 2024-08-28

Abstract

This SIP presents an architecture to enable Snaps to expose blockchain-specific methods to dapps, extending MetaMask’s functionality to support a multichain ecosystem.

Motivation

Currently, MetaMask is limited to EVM-compatible networks. This proposal aims to empower developers, both first- and third-party, to use Snaps to add native support for non-EVM-compatible chains within MetaMask.

Specification

Formal specifications are written in TypeScript.

Language

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” written in uppercase in this document are to be interpreted as described in RFC 2119.

High-Level architecture

The diagram below represents a high-level architecture of how the RPC Router integrates inside MetaMask to allow Snaps to expose protocol methods to dapps and the MetaMask clients.

High-level architecture

  • Account Snaps: Snaps that implement the Keyring API and are responsible for signing requests and managing accounts.

  • Protocol Snaps: Snaps that implement protocol methods that do not require an account to be executed.

  • RPC Router: Native component that forwards RPC requests to the appropriate Protocol Snap or Account Snap.

  • Keyring Controller: Native component responsible for forwarding signing requests to the appropriate keyring implementation.

  • Accounts Controller: Native component responsible for managing accounts inside MetaMask. It stores all non-sensitive account information.

  • Snaps Keyring: Native component that acts as a bridge between the Keyring Controller and the Account Snaps.

Components

Here is a brief description of the components involved in this architecture which will require to be implemented or modified.

RPC Router

The RPC Router will be a new native component responsible for routing JSON-RPC requests to the appropriate Snap or keyring.

To route a request, the RPC Router MUST extract the method name and CAIP-2 chainId from the request object. It then determines whether the method is supported by a Protocol Snap or an Account Snap, with Account Snaps taking precedence over Protocol Snaps.

If the method is supported by an Account Snap, the RPC Router forwards the request to the Keyring Controller; otherwise, it forwards the request to the appropriate Protocol Snap.

Snaps Keyring

The Snaps Keyring is an existing native component that exposes the snap_manageAccounts method, allowing Account Snaps to register, remove, and update accounts.

For example, this code can be used by an Account Snap to register a new account with the Account Router:

// This will notify the Account Router that a new account was created, and the
// Account Router will register this account as available for signing requests
// using the `eth_signTypedData_v4` method.
await snap.request({
  method: "snap_manageAccounts",
  params: {
    method: "notify:accountCreated",
    params: {
      account: {
        id: "74bb3393-f267-48ee-855a-2ba575291ab0",
        type: "eip155:eoa",
        address: "0x1234567890123456789012345678901234567890",
        methods: ["eth_signTypedData_v4"],
        options: {},
      },
    },
  },
});

Similar events are available to notify about the removal and update of accounts: notify:accountRemoved and notify:accountUpdated.

Additionally, the Snaps Keyring expects the Account Snap to implement the Keyring API so it can forward signing requests to it through the keyring_submitRequest method.

Account Snaps

As part of the Keyring API, non-EVM Account Snaps MUST also implement support for the keyring_resolveAccountAddress RPC method defined below. It is used by the RPC Router to extract the address of the account that should handle the signing request from the request object.

type ResolveAccountAddressRequest = {
  method: "keyring_resolveAccountAddress";
  params: {
    scope: CaipChainId;
    request: JsonRpcRequest;
  };
};

scope - The CAIP-2 chainId the request is targeting

request - A JsonRpcRequest containing strictly JSON-serializable values.

The implementation MUST return a value of the type { address: string } or null.

Protocol Snaps

Protocol Snaps implement and expose methods that do not require an account to execute and MUST list their supported methods and notifications in their manifest file:

"initialPermissions": {
  "endowment:protocol": {
    "scopes": {
      "<caip2_chainId>": {
        "methods": [
          // List of supported methods
        ],
        "notifications": [
          // List of supported notifications
        ]
      }
    }
  }
}

Additionally protocol Snaps MUST implement the onProtocolRequest handler:

import { OnProtocolRequestHandler } from "@metamask/snap-sdk";

export const onProtocolRequest: OnProtocolRequestHandler = async ({
  origin,
  scope,
  request,
}) => {
  // Return protocol responses
};

The interface for an onProtocolRequest handler function’s arguments is:

interface OnProtocolRequestArguments {
  origin: string;
  scope: CaipChainId;
  request: JsonRpcRequest;
}

origin - The origin making the protocol request (i.e. a dapp).

scope - The CAIP-2 chainId the request is targeting.

request - A JsonRpcRequest containing strictly JSON-serializable values.

Any JSON-serializable value is allowed as the return value for onProtocolRequest.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Daniel Rocha, Frederik Bolding, Alex Donesky, "SIP-26: Non-EVM protocol support [DRAFT]," Snaps Improvement Proposals, no. 26, August 2024. [Online serial]. Available: https://github.com/MetaMask/SIPs/blob/master/SIPS/sip-26.md