# [ERC6865] オンチェーンでEIP712のメッセージを可視化する仕組みを理解しよう！

> 現在、多くのプロトコルや分散型アプリケーション（dApp）は、EIP712（構造化データを署名するための標準仕様）をそれぞれ独自の「スキーマ（データの項目名や型の定義）」で使っています。

- URL: https://cardene.dev/articles/f36939a6cc9102eb1563
- 公開日: 2025-11-21
- 更新日: 2025-11-21
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

https://amzn.asia/d/gxvJ0Pw

今回は、**EIP712**形式のデータをオンチェーンで解析し、ユーザーの資産にどのような影響が生じるかを分かりやすく示すことで、安全で理解しやすい取引体験を実現する仕組みを提案している**ERC6865**についてまとめていきます！

:::message
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-6865

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

現在、多くのプロトコルや分散型アプリケーション（dApp）は、**EIP712**（構造化データを署名するための標準仕様）をそれぞれ独自の「**スキーマ（データの項目名や型の定義）**」で使っています。
しかし、各プロトコルごとにスキーマがバラバラなため、エコシステム全体として統一された見せ方ができず、どうしても「**不整合**」や「**分かりづらさ**」が発生してしまいます。
:::message
**EIP712**については以下の記事を参考にしてください。
:::

[\[EIP712\] 型付き構造化データのハッシュ化と署名の仕組みを理解しよう！](/articles/f54bc85cb66de5a54257)

そこで、**ERC6865**は、dAppが共通のインターフェースとして `visualizeEIP712Message` というコントラクトの読み取り関数を実装するという考え方を提案しています。

この関数は、以下のような役割を持ちます。

* 入力として「**ABIエンコードされた EIP712のメッセージ**」（**EIP712**の構造化データを、Ethereumの標準的なエンコード方式でバイナリに変換したもの）を受け取る
* そのメッセージをdApp / プロトコル自身が理解してパースし、「**ユーザーの資産にどのような影響があるか**」に重点を置いて整理した、共通フォーマットの構造化データを返す

ウォレット側は、この構造化データを受け取りユーザーにわかりやすいUIで表示できるようになります。
これにより、ウォレットごとにバラバラだった**EIP712**メッセージの表示が、dAppをまたいで一貫した体験として提供できるようになり、ユーザーはどのdAppとやり取りする場合でも「**何が起こるのか**」を共通の形式で理解しやすくなります。

## 動機

#### web3.0の拡大とユーザーリスク

web3.0が急速に広がることで、多くのチャンスや新しいサービスが生まれています。
一方で、それに伴ってフィッシング詐欺などのセキュリティリスクも増えています。

特に問題になるのは、ユーザーが「**自分が署名しようとしている内容をきちんと理解できていない**」状態になりがちな点です。
署名が意味する内容をユーザーがきちんと把握できれば、怪しいトランザクションを避けることができ、リスクをかなり減らせます。

### オフチェーンSDKのアプローチとその課題

この課題に取り組むため、まずはウォレット側で**EIP712**を解釈して表示するための「**オフチェーンSDK**」が開発されました。
このSDKはオープンソースで、ウォレットがさまざまなプロトコルのメッセージを視覚的にわかりやすくすることを目的としています。

しかし、このオフチェーン方式には、以下のような大きな課題がありました。

| 観点 | 課題の内容 |
| -------- | -------- |
| スケーラビリティ | **EIP712**を使っているすべてのプロトコルを洗い出し、それぞれのビジネスロジックを理解するのはほぼ不可能に近いこと。新しいプロトコルやアップデートもどんどん出てくるため、オフチェーンSDKだけで全てをカバーするのは現実的ではありません。 |
| 信頼性 | 各プロトコルのビジネスロジックを外部の開発者が完全に理解するのは難しく、実装の意図を誤解する可能性があります。その結果、実際の動きとずれた誤った可視化をしてしまうことがあり、これは「**何も表示しない**」よりも危険な場合があります。 
| 保守性 | web3.0のエコシステムは変化が激しく、プロトコルは機能追加やバグ修正などで頻繁にアップグレードされます。そのたびにオフチェーン側のSDKも追従する必要があり、保守コストが高くなります。   |

このように、ウォレット側のオフチェーン SDK によってすべての EIP-712 メッセージを正しく、かつ継続的に可視化する、というアプローチには限界がありました。

#### オンチェーンでの標準化という解決策

これらの課題を解消するために、**ERC6865**は「**オンチェーンで標準化された仕組み**」を導入する考え方を取ります。
具体的には、**EIP712**メッセージの解釈と可視化の責任をウォレットからプロトコル側へ移す、という発想です。

プロトコルごとに `visualizeEIP712Message` を実装し、そのプロトコル自身が**EIP-712**メッセージを解析して、共通フォーマットの構造化データとして返すようにします。
これにより、以下のようなメリットが生まれます。

* スケーラビリティ
    新しいプロトコルが登場しても、そのプロトコル自身が `visualizeEIP712Message` を実装すればよく、ウォレットやSDKが個別に対応する必要がなくなります。
* 信頼性
    ビジネスロジックを一番正確に知っているのは、そのプロトコルの開発者自身です。プロトコル側が直接可視化用のデータを返すことで、「**実装と可視化のずれ**」が起こりにくくなります。
* 保守性
    プロトコルがアップグレードされた場合でも、そのプロトコルのコントラクト内で `visualizeEIP712Message` を更新すればよく、ウォレット側のコードを都度書き換える必要がありません。

このように、オンチェーンでの標準化を行うことで、拡張性・信頼性・保守性のいずれの観点からも状態が改善され、変化が激しい web3.0 の世界にも対応しやすくなります。

#### ウォレットエコシステム全体への効果

**ERC6865**が広く採用されると、ウォレット開発者にとっては以下のようなメリットがあります。

* 各プロトコルごとの独自実装をウォレット側で追いかける必要が減る
* 新しいプロトコルへの対応が早くなり、カバー範囲も広げやすくなる
* メンテナンスにかかる負担を大きく減らせる

そしてこれは、そのままユーザー体験の向上につながります。
ユーザーは、どのウォレット・どのプロトコルを使っていても、**EIP712**メッセージの内容を「**同じような形式**」で理解できるようになり、自分の資産がどう動くのかをより正確に把握できます。
結果として、セキュリティ面の向上と、全体としての使いやすさの向上の両方が期待できます。

#### 現状のウォレット表示と「可視化」後の違い

現在、多くのウォレットは**EIP712**メッセージを、かなり機械的な情報として表示しています。
例えば、型情報やフィールド名、値がそのまま並んでいるだけで、ユーザーにとって「**何が起こるのか**」を直感的に理解しにくいUIになっていることが多いです。

これに対して、**ERC6865**の仕組みを使って `visualizeEIP712Message` から構造化データを受け取り、それに基づいて表示すると、ユーザーには以下のような形で「**意味のある情報**」を出せるようになります。

| 観点 | 現在の表示 | **ERC6865** 採用後の表示イメージ |
| ------- | --------- | ------- |
| 情報の粒度   | フィールド名と値が生のまま並ぶ     | 「**どのトークンが、誰から誰へ、いくら動くか**」、「**どんなポジションが開かれるか**」など、ユーザーの資産への影響を中心に整理された情報 |
| 理解のしやすさ | 技術者でないと理解が難しい  | 一般ユーザーでも直感的に理解しやすい表現      |
| 一貫性     | プロトコルごとに表示の雰囲気がバラバラ | プロトコルが違っても、ウォレット側は共通フォーマットをもとに一貫したUIを提供可能  |

![image.png](/images/qiita-cc6dc38c-ab35-42c8-86a7-ba5324f43e78.webp)
https://eips.ethereum.org/EIPS/eip-6865

![image.png](/images/qiita-c3055602-7420-4f6c-b632-12b4fba63288.webp)
https://eips.ethereum.org/EIPS/eip-6865

## 仕様

**ERC6865**では、**EIP712**のメッセージを署名する時、ユーザーが「**メッセージの内容を理解できない**」問題を解消するため、ウォレットがオンチェーンから可視化用データを取得できる仕組みを定めています。

#### `verifyingContract` と `chainId` の扱い

**EIP712**のメッセージには「**ドメインセパレーター**」という識別情報があり、その中に以下の2つが含まれます。

| 項目                    | 説明                        |
| --------------------- | ------------------------- |
| **verifyingContract** | メッセージを検証するコントラクトのアドレス。    |
| **chainId**   | どのネットワークで有効なメッセージかを識別する値。 |

**ERC6865**では、ウォレットがユーザーに署名を求められたとき、以下の条件が満たされていればコントラクトを呼び出して可視化データを取得できます。

* `verifyingContract` が指定されていること
* `chainId` が指定されていること

もしメッセージにこれらが含まれていない場合、ウォレットは可視化処理を無視するべきとされています。

#### `visualizeEIP712Message` の役割

`visualizeEIP712Message` は、**ERC6865**の中心となる関数です。
プロトコル側がこの関数を実装することで、ウォレットはオフチェーンで複雑な解析をせずとも、オンチェーンで正確な可視化情報を得ることができます。

この関数は、ABIでエンコードされた**EIP712**メッセージを受け取り、以下の3種類の情報を返します。

* ユーザーが受け取る資産（assetsIn）
* ユーザーが提供する資産（assetsOut）
* メッセージの有効期間（liveness）

これらをウォレットがUIに表示することで、ユーザーは「**どの資産が増え、どの資産が減り、いつまで有効なのか**」を明確に理解できます。

#### パラメータ説明

##### `encodedMessage`

ABI 形式でエンコードされた **EIP712**メッセージです。
`abi.decode` を用いて元の構造化データに復元できます。

##### `domainHash`

**EIP712**の定義に沿って計算されたドメインセパレーターのハッシュ値です。
ウォレット側が対象コントラクトを確認するために使います。

#### 出力仕様

```js
struct Liveness {
  uint256 from;
  uint256 to;
}

struct UserAssetMovement {
  address assetTokenAddress;
  uint256 id;
  uint256[] amounts;
}

struct Result {
  UserAssetMovement[] assetsIn;
  UserAssetMovement[] assetsOut;
  Liveness liveness;
}
```

関数は**Result**を返します。
`Result` は以下の3要素で構成されます。

##### `Liveness`

メッセージがいつ有効かを示すタイムスタンプです。

| フィールド  | 説明            |
| ------ | ------------- |
| `from` | メッセージの有効開始時刻。 |
| `to`   | メッセージの有効終了時刻。 |

`from < to` である必要があります。

有効期間のある署名メッセージはDeFiプロトコルに多く、ウォレットがこれを表示することで、ユーザーは期限切れリスクを直感的に理解できます。

##### `UserAssetMovement`

資産の種類・数量・時間軸ごとの変動量を表す構造です。

| フィールド  | 説明  |
| ------------------- | ---------- |
| `assetTokenAddress` | トークンのアドレス。ゼロアドレスはネイティブコイン。|
| `id`   | NFTのトークンID。NFTでない場合は無視。  |
| `amounts`  | 有効期間に対応した資産量。最初が `from`、最後が `to`。ほとんどのケースでは 1 要素のみ。 |

NFT かどうかに応じて意味が変わる点が特徴です。

##### `assetsIn`

ユーザーが「**最低限受け取るはずの資産**」を意味します。
プロトコルが期待する結果ではなく、ユーザー視点での最小量という点が重要です。

##### `assetsOut`

ユーザーが「**最大で支払う必要のある資産**」を意味します。
ウォレットは `assetsOut` を強調表示することで、ユーザーが不注意な署名を回避できます。

### インターフェース

```js
/**
* @notice This function processes an EIP-712 payload message and returns a structured data format emphasizing the potential impact on users' assets.
* @dev The function returns assetsOut (assets the user is offering), assetsIn (assets the user would receive), and liveness (validity duration of the EIP-712 message).
* @param encodedMessage The ABI-encoded EIP-712 message (abi.encode(types, params)).
* @param domainHash The hash of the EIP-712 domain separator as defined in the EIP-712 proposal; see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
* @return Result struct containing the user's assets impact and message liveness.
*/
function visualizeEIP712Message(
    bytes memory encodedMessage,
    bytes32 domainHash
) external view returns (Result memory);

struct Liveness {
  uint256 from;
  uint256 to;
}

struct UserAssetMovement {
  address assetTokenAddress;
  uint256 id;
  uint256[] amounts;
}

struct Result {
  UserAssetMovement[] assetsIn;
  UserAssetMovement[] assetsOut;
  Liveness liveness;
}
```

#### 構造体

##### `Liveness`

```javascript
struct Liveness {
  uint256 from;
  uint256 to;
}
```

メッセージが有効な期間を示す構造体。
署名メッセージの有効期間を表し、開始時刻と終了時刻の2つの値を保持します。`from` は必ず `to` より小さくなります。

**パラメータ**

* `from`
  * 有効開始時刻。
* `to`
  * 有効終了時刻。

##### `UserAssetMovement`

```javascript
struct UserAssetMovement {
  address assetTokenAddress;
  uint256 id;
  uint256[] amounts;
}
```

ユーザーの資産量を時間軸で示す構造体。
**ERC20**・**ERC721**・**ERC1155**などの資産の識別と、時間ごとの数量を示すために使われます。
NFTの場合は `id` が意味を持ちます。

:::message
**ERC20**については以下の記事を参考にしてください。
:::

[\[ERC20\] ERC20を1からわかりやすく解説](/articles/aeefe2d89dd5332c9f43)

:::message
**ERC721**については以下の記事を参考にしてください。
:::

[\[ERC721\] ERC721を1からわかりやすく解説](/articles/0570a8d05bc7c9b9593f)

:::message
**ERC1155**については以下の記事を参考にしてください。
:::

[\[ERC1155\] ERC1155を1からわかりやすく解説](/articles/c3ef92722b057161bebd)

**パラメータ**

* `assetTokenAddress`
  * 資産のトークンアドレス。
  * ゼロアドレスはネイティブコイン。
* `id`
  * NFTのトークンID。
* `amounts`
  * 各タイムスタンプ時点の資産量。

##### `Result`

```javascript
struct Result {
  UserAssetMovement[] assetsIn;
  UserAssetMovement[] assetsOut;
  Liveness liveness;
}
```

ユーザーの資産の入出、そしてメッセージ有効期間をまとめた構造体。
ウォレットがUI表示する際に必要な情報を1つに合わせた形式です。
受け取る資産、提供する資産、有効期間の3つが含まれます。

**パラメータ**

* `assetsIn`
  * 最低限受け取る資産。
* `assetsOut`
  * 最大で提供する資産。
* `liveness`
  * メッセージの有効期間。

#### 関数

##### `visualizeEIP712Message`

```javascript
function visualizeEIP712Message(
    bytes memory encodedMessage,
    bytes32 domainHash
) external view returns (Result memory);
```

**EIP712**メッセージを解析して資産変動を返す関数。
ウォレットが署名前にユーザーへ視覚的な説明を行うための情報を、オンチェーンから取得するための関数です。
プロトコル自身がこの関数を実装するため、ビジネスロジックとの乖離が発生しません。

**引数**

* `encodedMessage`
  * **EIP712**メッセージをABIエンコードしたもの。
* `domainHash`
  * ドメインセパレーターのハッシュ。

**戻り値**

* `Result`
  * 資産の移動情報と有効期間。

## 補足

#### on-chainの必要性

**EIP712**の可視化は一見するとオフチェーンでも実現できそうに思えますが、実際の運用経験からはオンチェーンで仕組みを提供する必要があります。
特に**ERC6865**では、開発者が直接コントラクト内で可視化ロジックを実装することに意味があります。
これは、実際にオフチェーン SDK（TypeScript）を開発して分かった限界が根拠となっています。

以下がその理由です。

##### 信頼性の問題

プロトコルの仕組みを最も理解しているのは、そのプロトコルを実装した開発者です。
第三者が外部で可視化処理を作ると、仕様理解が不十分なまま実装してしまう可能性があり、可視化結果が実際の挙動とずれることがあります。
このずれは、ユーザーに誤った情報を提示することにつながり、セキュリティ上も危険です。

オンチェーンでプロトコル自身が可視化を提供することで、実装との矛盾が生まれず、より正確な情報を提供できます。

##### スケーラビリティの問題

web3のプロトコルは非常に速いペースで増えています。
ウォレットや第三者がすべてのプロトコルを理解し、**EIP712**メッセージの可視化ロジックを個別に実装していくのは現実的ではありません。

* 新しいプロトコルの登場
* プロトコルのアップグレード
* ビジネスロジックの変更

これらすべてに外部が追従するのは困難であり、可視化の網羅性も維持しづらくなります。
オンチェーンでプロトコル自身が対応する仕組みを持てば、ウォレット側は統一仕様に従うだけで良くなり、対応範囲と速度は大きく改善されます。

##### 保守性の問題

多くのプロトコルはアップグレード可能なスマートコントラクトを採用しています。
そのため、外部がオフチェーン可視化を実装しても、プロトコルがバージョンアップすると挙動が変わり、外部実装が正しくなくなることがあります。

このズレは信頼性を損ない、メンテナンスコストも高くなります。
オンチェーンで可視化を提供する形なら、プロトコルが内部的に更新するだけで常に最新の正しい情報を返せるため、保守性が大幅に向上します。

#### `DomainHash` の役割

`domainHash` は、**EIP712**メッセージのドメインセパレーターをハッシュ化したものです。
**ERC6865**では、この値が非常に重要な意味を持っています。

理由は以下です。

##### 異なるバージョンの判別

プロトコルが複数の**EIP712**仕様をサポートしている場合、`domainHash` によって使用すべきバージョンを区別できます。
もし `domainHash` が対応していないものであれば、処理を中断して誤った実装を実行しないようにできます。

##### 他プロトコルとの混同防止

異なるプロトコルの `domainHash` が使われた場合、誤った期待値を返す危険があります。
これを防ぐために、`domainHash` が一致しなければリバートし、正しいプロトコルでないことを明確にできます。

##### 既存プロトコルの将来的な再ルーティングへの対応

将来的に、既にデプロイ済みでアップグレードできないプロトコルを外部レジストリでルーティングする仕組みができた場合、`domainHash` はプロトコル識別に利用できる指標になります。

#### Amounts Arrayを採用する理由

**ERC6865**では `amounts` を `uint256[]`（配列）として定義しています。
これは単に「**複数の値を扱えるようにする**」ためではありません。

主な理由は、NFT系のプロトコルでよく使われる**オークション**を扱うためです。

オークションでは、価格や必要資産量が時間によって変動します。
そのため、以下の点が重要になります。

| 要素                  | 意味                     |
| ------------------- | ---------------------- |
| 最初の値（amounts[0]）    | `liveness.from` 時点の必要量 |
| 最後の値（amounts[last]） | `liveness.to` 時点の必要量   |
| 中間に値が入る可能性          | オークションの価格曲線に合わせるため     |

ほとんどのケースでは `amounts` は1要素ですが、オークションのように時間変化するプロトコルにも柔軟に対応できるように配列形式が採用されています。

## セキュリティ

**ERC6865**では、ユーザーが署名する**EIP712**メッセージの内容を「**正しく、信頼できる形で表示する**」ことを目的としています。
そのため、以下の点がセキュリティ上特に重要になります。

#### `visualizeEIP712Message` の信頼性

`visualizeEIP712Message` が返す情報は、ユーザーが何に署名するかを判断するうえで非常に重要です。
この結果が間違っていた場合、ユーザー資産が予期しない形で移動する危険があります。

そのため、プロトコルは正確で最新の情報を返すように実装しなければなりません。

#### `verifyingContract` の信頼性に依存する

この仕組みでは、可視化関数は `verifyingContract` と同じアドレスで動作します。
つまり、署名メッセージの検証に使われるコントラクトと、可視化結果を返すコントラクトは同一です。

これは以下の意味を持ちます。

* `verifyingContract` が信頼できるプロトコルであれば、可視化結果も信頼できる
* `verifyingContract` 自体が怪しいプロトコルであれば、可視化結果も信用してはいけない

ウォレット側は、可視化関数の信頼性を単独で判断するのではなく、必ず**`verifyingContract` の信用度**を基準として扱う必要があります。

## 引用

Abderrahmen Hanafi (@a6-dou), "ERC-6865: On-Chain EIP-712 Visualization [DRAFT]," Ethereum Improvement Proposals, no. 6865, April 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6865.

## 最後に

今回は「**EIP712**形式のデータをオンチェーンで解析し、ユーザーの資産にどのような影響が生じるかを分かりやすく示すことで、安全で理解しやすい取引体験を実現する仕組みを提案している**ERC6865**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
