Skip to content

Commit

Permalink
Add Snaps Keyring API documentation (MetaMask#852)
Browse files Browse the repository at this point in the history
* Test typedoc + git submodule setup

* checkout submodules in github actions

* update lavamoat config to allow postinstall

* Add snaps keyring api documentation

* update keyring api docs

* Complete basic Keyring API docs

* update prettier

* update submodules for build/lint

* ignore external in eslint

* Keyring doc rearrangement (MetaMask#907)

* remove 4337 mentions, add flask mention

* update typedoc plugin to @next version

provides better formatting for type aliases

* Remove mention of 4337 in accounts-ui image

* fix broken links

* fix more broken links

* fix linting

* edit md content and images

* remove extra submodule

* feat: add KeyringSnapRpcClient documentation (MetaMask#917)

* feat: add KeyringSnapRpcClient documentation

* fix: add `KeyringSnapRpcClient` reference links

* edit content

---------

Co-authored-by: Alexandra Tran <[email protected]>

---------

Co-authored-by: Harpal Jadeja <[email protected]>
Co-authored-by: Alexandra Tran <[email protected]>
  • Loading branch information
3 people authored Sep 22, 2023
1 parent f1845a2 commit c3debe9
Show file tree
Hide file tree
Showing 27 changed files with 882 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/build
/.yarn
/.yarn
/external/keyring-api
6 changes: 6 additions & 0 deletions .github/workflows/build-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Use Node.js
uses: actions/setup-node@v3
with:
Expand All @@ -24,6 +26,8 @@ jobs:
- prepare
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Use Node.js
uses: actions/setup-node@v3
with:
Expand All @@ -46,6 +50,8 @@ jobs:
- prepare
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Use Node.js
uses: actions/setup-node@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
uses: actions/checkout@v3
with:
ref: ${{ inputs.ref }}
submodules: true
- name: Use Node.js
uses: actions/setup-node@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.docusaurus
.cache-loader
.idea
/snaps/reference/keyring-api

# yarn v3 (w/o zero-install)
# See: https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "external/keyring-api"]
path = external/keyring-api
url = [email protected]:MetaMask/keyring-api.git
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"eslint.format.enable": true,
"eslint.packageManager": "yarn",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
Expand Down
14 changes: 14 additions & 0 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const codeTheme = require("prism-react-renderer/themes/dracula");
const remarkCodesandbox = require("remark-codesandbox");
const path = require("path");

/** @type {import('@docusaurus/types').Config} */
const config = {
Expand Down Expand Up @@ -79,6 +80,19 @@ const config = {
],
}),
],
[
"docusaurus-plugin-typedoc",
{
entryPoints: ["./external/keyring-api/src/index.ts"],
tsconfig: "./external/keyring-api/tsconfig.json",
out: path.join(__dirname, "snaps/reference/keyring-api"),
sidebar: {
categoryLabel: "Keyring API",
position: 99,
},
identifiersAsCodeBlocks: true,
},
],
[
"@docusaurus/plugin-client-redirects",
{
Expand Down
1 change: 1 addition & 0 deletions external/keyring-api
Submodule keyring-api added at 1c8eeb
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
"lint:fix": "eslint . --fix",
"postinstall": "cd external/keyring-api && yarn install"
},
"dependencies": {
"@docusaurus/core": "2.4.1",
Expand All @@ -24,12 +25,17 @@
"@metamask/design-tokens": "^1.11.1",
"@metamask/docusaurus-openrpc": "^0.2.2",
"clsx": "^1.2.1",
"docusaurus-plugin-typedoc": "next",
"node-polyfill-webpack-plugin": "^2.0.1",
"prettier": "^3.0.0",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"remark-codesandbox": "^0.10.1",
"remark-docusaurus-tabs": "^0.2.0"
"remark-docusaurus-tabs": "^0.2.0",
"typedoc": "^0.25.1",
"typedoc-plugin-frontmatter": "^0.0.2",
"typedoc-plugin-markdown": "next"
},
"devDependencies": {
"@docusaurus/eslint-plugin": "2.4.1",
Expand Down Expand Up @@ -75,7 +81,8 @@
"@metamask/docusaurus-openrpc>@metamask/open-rpc-docs-react>@stoplight/mosaic>@fortawesome/fontawesome-svg-core": false,
"@metamask/docusaurus-openrpc>@metamask/open-rpc-docs-react>@stoplight/mosaic>@fortawesome/fontawesome-svg-core>@fortawesome/fontawesome-common-types": false,
"@docusaurus/core>webpack-dev-server>ws>bufferutil": false,
"@docusaurus/core>webpack-dev-server>ws>utf-8-validate": false
"@docusaurus/core>webpack-dev-server>ws>utf-8-validate": false,
"$root$": false
}
}
}
Binary file added snaps/assets/keyring/accounts-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snaps/assets/keyring/add-snap-account.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snaps/assets/keyring/asynchronous-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snaps/assets/keyring/components-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snaps/assets/keyring/create-account-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snaps/assets/keyring/synchronous-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
166 changes: 166 additions & 0 deletions snaps/concepts/keyring-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
description: Learn about the Snaps Keyring API.
sidebar_position: 6
---

# About the Keyring API

:::caution important
This API is only available in [MetaMask Flask](../get-started/install-flask.md), the canary
distribution of MetaMask.
:::

:::tip API documentation
See the [Keyring API reference](../reference/keyring-api/index.md) for all the Keyring API methods.
:::

The Snaps Keyring API integrates custom EVM accounts inside MetaMask.
Previously, you needed a companion dapp to display custom EVM accounts, such multi-party computation
(MPC) accounts.
Now you can display these custom accounts alongside regular MetaMask accounts in the UI:

<p align="center">
<img src={require('../assets/keyring/accounts-ui.png').default} alt="Keyring snap accounts in Metamask UI" width="360" />
</p>

[Create a Keyring snap to integrate custom EVM accounts in MetaMask.](../tutorials/custom-evm-accounts.md)
Your dapp can then use the [`eth_requestAccounts`](/wallet/reference/eth_requestaccounts) MetaMask
JSON-RPC API method to connect to the custom accounts, and seamlessly interact with them using other
[JSON-RPC methods](/wallet/reference/eth_subscribe).

## Terminology

The following terminology is used across the Keyring API:

- **Blockchain account**: An object in a single blockchain, representing an account, with its
balance, nonce, and other account details.
- **Request**: A request from a dapp to MetaMask.
- **Keyring account**: An account model that represents one or more blockchain accounts.
- **Keyring snap**: A snap that implements the Keyring API.
- **Keyring request**: A request from MetaMask to a Keyring snap.
MetaMask wraps the original request sent by the dapp and adds some metadata to it.

## Components diagram

The following diagram shows the components you encounter when interacting with accounts managed by a
Keyring snap:

<p align="center">

![Keyring snap component diagram](../assets/keyring/components-diagram.png)

</p>

- **User**: The user interacting with the snap, the dapp, and MetaMask.
- **Dapp**: The dapp requesting an action to be performed on an account.
- **MetaMask**: The wallet the dapp connects to.
MetaMask routes requests to the Keyring snaps and lets the user perform some level of account management.
- **Snap**: A snap that implements the Keyring API to manage the user's accounts, and to handle
requests that use these accounts.
- **Snap UI**: The snap's UI component that allows the user to interact with the snap to perform
custom operations on accounts and requests.

## Keyring interface

The first step to create a Keyring snap is to implement the
[`Keyring`](../reference/keyring-api/03-Type%20Aliases/02-type-alias.Keyring.md) interface.
This interface describes all the methods necessary to make your custom EVM accounts work inside
MetaMask with your own logic.

The following sections describe the different flows that the `Keyring` interface handles.

### Snap account creation flow

The first interaction between users and the Keyring snap is the snap account creation process.
The flow looks like the following:

![Keyring snap account creation flow](../assets/keyring/create-account-flow.png)

The MetaMask account selection modal has an option called **Add snap account**:

<p align="center">
<img src={require('../assets/keyring/add-snap-account.png').default} alt="Add snap account option" width="360" />
</p>

This option shows a list of Keyring snaps.
Each snap redirects the user to the companion dapp that contains all the UI to configure and manage the snap.

The dapp presents a custom UI allowing the user to configure their custom EVM account.
The dapp uses the [`createAccount`](../reference/keyring-api/02-Classes/04-class.KeyringSnapRpcClient.md#createaccount)
method of the `KeyringSnapRpcClient`, which calls the `Keyring` interface's method of the same name.
You can find an example of this in the [example Keyring snap companion dapp](https://github.com/MetaMask/snap-simple-keyring/blob/d3f7f0156c59059c995fea87f90a3d0ad3a4c135/packages/site/src/pages/index.tsx#L136).

The `createAccount` method of the `Keyring` interface creates the account based on the parameters passed
to the method.
The snap keeps track of the accounts that it creates using [`snap_manageState`](../reference/rpc-api.md#snap_managestate).
Once the snap has created an account, it notifies MetaMask using the
[`createAccount`](../reference/rpc-api.md#createaccount) sub-method of `snap_manageAccounts`.
You can find an example of this process in the
[example companion dapp](https://github.com/MetaMask/snap-simple-keyring/blob/d3f7f0156c59059c995fea87f90a3d0ad3a4c135/packages/snap/src/keyring.ts#L61).

Once the snap has created an account, that account can be used to sign messages and transactions.

### Synchronous signing flow

If the Keyring snap can sign transactions directly, it implements a simple synchronous signing flow.
If the snap needs a third party such as a hardware key or a second account's signature (for example,
in a threshold signature scheme), it implements an [asynchronous signing flow](#asynchronous-signing-flow).
The synchronous flow looks like the following:

![Synchronous signing flow](../assets/keyring/synchronous-flow.png)

See the [example Keyring snap companion dapp](https://github.com/MetaMask/snap-simple-keyring) for a
full example.

The flow starts when a dapp calls a [MetaMask JSON-RPC method](/wallet/reference/eth_subscribe), or
when the user initiates a new funds transfer from the MetaMask UI.
At that point, MetaMask detects that this interaction is requested for an account controlled by the
Keyring snap.

After the user approves the transaction in the UI, MetaMask calls the `submitRequest` method of the
`Keyring` interface.
`submitRequest` receives the original RPC request, and returns a
[`SubmitRequestResponse`](../reference/keyring-api/04-Variables/05-variable.SubmitRequestResponseStruct.md)
with `pending` set to `false`, and `result` set to the requested signature.

:::caution important
If the Keyring snap receives an
[`eth_sendTransaction`](/wallet/reference/eth_sendTransaction) request, it should treat it like an
[`eth_signTransaction`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_signtransaction) request.
That is, the snap is responsible for providing the signature in the response, and MetaMask is
responsible for broadcasting the transaction.
:::

### Asynchronous signing flow

If the Keyring snap implements a complex scheme such as threshold signing, it implements an
asynchronous signing flow with more `Keyring` methods.
The asynchronous flow looks like the following:

![Asynchronous signing flow](../assets/keyring/asynchronous-flow.png)

The flow starts the same way as the [synchronous flow](#synchronous-signing-flow): a dapp or user
initiates a request to sign a transaction or arbitrary data.
After approval, the `submitRequest` method of the snap's `Keyring` interface is called.

Since the snap doesn't answer the request directly, it stores the pending request in its internal
state using [`snap_manageState`](../reference/rpc-api.md#snap_managestate).
This list of pending requests is returned when the `listRequests` or `getRequest` methods of the
`Keyring` interface are called.

After storing the pending request, the snap creates a pop-up using
[`snap_dialog`](../reference/rpc-api.md#snap_dialog) instructing the user to go to the companion
dapp's URL.

The dapp lists the snap's pending requests using an RPC call facilitated by the
[`listRequests`](../reference/keyring-api/02-Classes/04-class.KeyringSnapRpcClient.md#listrequests)
method of the `KeyringSnapRpcClient`.
The user can then act on those requests using whatever process applies to the snap.

Once the signing process completes, the companion dapp resolves the request using the
[`approveRequest`](../reference/keyring-api/02-Classes/04-class.KeyringSnapRpcClient.md#approverequest)
method of the `KeyringSnapRpcClient`, which calls the `Keyring` interface's method of the same name.
This method receives the request's ID and final result.

When `approveRequest` is called, it can resolve the pending request by using the
[`submitResponse`](../reference/rpc-api.md#submitresponse) sub-method of `snap_manageAccounts`.
2 changes: 1 addition & 1 deletion snaps/how-to/troubleshoot.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Solve common issues.
sidebar_position: 6
sidebar_position: 7
---

# Troubleshoot
Expand Down
Loading

0 comments on commit c3debe9

Please sign in to comment.