Skip to content

Commit

Permalink
Add signature insights documentation (#1103)
Browse files Browse the repository at this point in the history
  • Loading branch information
ziad-saab authored Jan 25, 2024
1 parent f84cb9a commit 20ba995
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 0 deletions.
Binary file added snaps/assets/signature-insights-permission.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/signature-insights-warning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 139 additions & 0 deletions snaps/how-to/provide-signature-insights.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
description: Provide insights to your users in MetaMask's signature confirmation flow.
sidebar_position: 10
---

# Provide signature insights

:::flaskOnly
:::


Similarly to [providing transaction insights](../tutorials/transaction-insights.md), Snaps allows you to provide signature insights to your users.

## Steps

### 1. Request permission to display signature insights

To display signature insights to your users, you first need to add the [`endowment:signature-insight`](../reference/permissions.md#endowmentsignature-insight) permission to your Snap's manifest:

```json title="snap.manifest.json"
{
"initialPermissions": {
"endowment:signature-insight": {}
}
}
```

Additionally, if you need to receive the origin of the signature request, you must add `allowSignatureOrigin` to the permission object, and set it to `true`:

```json title="snap.manifest.json"
{
"initialPermissions": {
"endowment:signature-insight": {
"allowSignatureOrigin": true
}
}
}
```

When requesting the permission, this is how it will be displayed in the MetaMask UI when installing the Snap:

![Signature insights permission](../assets/signature-insights-permission.png)

### 2. Implement `onSignature` and export it from `index.ts`

The next step is for your Snap to export an `onSignature` function. This function receives a `signature` object. The shape of this object depends on the chain, as well as on the signing method used. This is why it's typed as `Record<string, unknown>`.

For Ethereum and Ethereum-compatible chains, the `signature` object can have one of the following shapes, depending on the signging method used:

#### eth_sign

```typescript
interface EthSignature {
from: string;
data: string;
signatureMethod: 'eth_sign';
}
```

#### personal_sign

```typescript
interface PersonalSignature {
from: string;
data: string;
signatureMethod: 'personal_sign';
}
```

#### eth_signTypedData

```typescript
interface SignTypedDataSignature {
from: string;
data: Record<string, any>[];
signatureMethod: 'eth_signTypedData';
}
```

#### eth_signTypedData_v3

```typescript
interface SignTypedDataV3Signature {
from: string;
data: Record<string, any>;
signatureMethod: 'eth_signTypedData_v3';
}
```

#### eth_signTypedData_v4

```typescript
interface SignTypedDataV4Signature {
from: string;
data: Record<string, any>;
signatureMethod: 'eth_signTypedData_v4';
}
```

Your Snap should use `signatureMethod` as the source of the truth to identify the signature scheme it is providing insights for.

Once you've identified the signature object, your Snap may run any logic it wants, including calling APIs. Then, your Snap must either return `null` if it has no insights to provide, or an object with a `content` property and an optional `severity` property.

The `content` object must be an instance of [Custom UI](./use-custom-ui.md). The `severity`, if provided, must be an instance of the `SeverityLevel` enum exported by the `@metamask/snaps-sdk` package. Currently the only severity level available is `SeverityLevel.Critical`.

:::caution
Due to current MetaMask UI limitations, signature insights will only be displayed if your Snap's logic deems the signature to be one that a user shouldn't sign, that is if you return a severity level of `SeverityLevel.Critical`.
:::

Here's an example implementation of `onSignature`:

```typescript
import { OnSignatureHandler, SeverityLevel } from '@metamask/snaps-types';
import { panel, heading, text } from '@metamask/snaps-ui';

export const onSignature: OnSignatureHandler = async ({
signature,
signatureOrigin,
}) => {
const insights = /* Get insights based on custom logic */;
return {
content: panel([
heading('My Signature Insights'),
text('Here are the insights:'),
...(insights.map((insight) => text(insight.value)))
]),
severity: SeverityLevel.Critical
};
};
```

When your Snap returns a signature insight with a `severity` of `SeverityLevel.Critical`, the Custom UI will be displayed in a modal after the user presses the "Sign" button, like so:

![Signature insights warning](../assets/signature-insights-warning.png)

## Reference

- [`endowment:signature-insight`](../reference/permissions.md#endowmentsignature-insight)
- [`onSignature` export](../reference/exports.md#onsignature)
78 changes: 78 additions & 0 deletions snaps/reference/exports.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,84 @@ module.exports.onTransaction = async ({

<!--/tabs-->

## onSignature

:::flaskOnly
:::

To provide signature insights before a user signs a message, a Snap must export `onSignature`.
Whenever any of the message signature methods like `personal_sign` or `eth_signTypedData_v4`, MetaMask calls this method.
MetaMask passes the raw unsigned signature payload to the `onSignature` handler method.

:::note
For MetaMask to call the Snap's `onSignature` method, you must request the
[`endowment:signature-insight`](permissions.md#endowmentsignature-insight) permission.
:::

#### Parameters

An object containing:

- `signature` - The raw signature payload.
- `signatureOrigin` - The signature origin if
[`allowSignatureOrigin`](permissions.md#endowmentsignature-insight) is set to `true`.

#### Returns

- An optional `severity` property that, if present, must be set to `SeverityLevel.Critical`
- A `content` object displayed using [custom UI](../how-to/use-custom-ui.md) after the user presses the "Sign" button. At this time due to limitations of MetaMask's signature confirmation UI, the content will only be displayed if the `severity` property is present and set to `SeverityLevel.Critical`.

#### Example

<!--tabs-->

# TypeScript

```typescript
import { OnSignatureHandler, SeverityLevel } from '@metamask/snaps-types';
import { panel, heading, text } from '@metamask/snaps-ui';

export const onSignature: OnSignatureHandler = async ({
signature,
signatureOrigin,
}) => {
const insights = /* Get insights */;
return {
content: panel([
heading('My Signature Insights'),
text('Here are the insights:'),
...(insights.map((insight) => text(insight.value)))
]),
severity: SeverityLevel.Critical
};
};
```

# JavaScript

```js
import { SeverityLevel } from '@metamask/snaps-sdk';
import { panel, heading, text } from '@metamask/snaps-ui';

module.exports.onSignature = async ({
signature,
signatureOrigin,
}) => {
const insights = /* Get insights */;
return {
content: panel([
heading('My Signature Insights'),
text('Here are the insights:'),
...(insights.map((insight) => text(insight.value)))
]),
severity: SeverityLevel.Critical
};
};
```

<!--/tabs-->


## onCronjob

To run periodic actions for the user (cron jobs), a Snap must export `onCronjob`.
Expand Down
21 changes: 21 additions & 0 deletions snaps/reference/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,27 @@ Specify this permission in the manifest file as follows:
},
```

### endowment:signature-insight

To provide signature insights, a Snap must request the `endowment:signature-insight` permission.
This permission grants a Snap read-only access to raw signature payloads, before they're accepted
for signing by the user, by exporting the [`onSignature`](../reference/exports.md#onsignature) method.

This permission requires an object with an `allowSignatureOrigin` property to signal if the Snap
should pass the `signatureOrigin` property as part of the `onSignature` parameters.
This property represents the signature initiator origin.
The default is `false`.

Specify this permission in the manifest file as follows:

```json
"initialPermissions": {
"endowment:signature-insight": {
"allowSignatureOrigin": true
}
},
```

### endowment:webassembly

To use WebAssembly, a Snap must request the `endowment:webassembly` permission.
Expand Down

0 comments on commit 20ba995

Please sign in to comment.