[ERC6315] 信頼不要のメタトランザクションの仕組みを理解しよう！

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、フォワーダーを信頼する必要のない仕組みを提案している、**ERC2771**の変種規格である**ERC6315**についてまとめていきます！

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

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

:::note warn
**6315**は現在（2023年10月13日）では「**Draft**」段階です。
:::

# 概要

「**ERC2771**」は、メタトランザクションにおける共通の標準で、1つ以上の信頼できるフォワーダー（転送者）を使用します。
このEIP（Ethereum Improvement Proposal、イーサリアムの改善提案）は、「**ERC2771**」を拡張し、信頼できない（トラストレス）アカウント抽象化をサポートするためのものです。

:::note info
「**ERC2771**」は、トランザクションを実行する時に、通常の方法ではなく、信頼できるフォワーダーを介して操作を実行する方法を提供する規格です。
しかし、この提案では、信頼できない状況でもアカウントの抽象化を行う方法を追加します。
つまり、セキュリティや信頼性の観点から、新しい方法でトランザクションを処理できるようになります。
:::

この拡張により、イーサリアムのスマートコントラクトをより柔軟に操作でき、トランザクションの処理方法を向上させることが可能になります。

:::note info
**ERC2771**については以下を参考にしてください。
:::

https://qiita.com/cardene/items/e84f53471b7a79240f33

# 仕様

この文書における「**Trusted Forwarder（信頼できるフォワーダー）**」と「**Recipient（受信者）**」というキーワードは、**ERC2771**の説明に従って解釈されるべきです。

## Forwaderインターフェース

```javascript
pragma solidity ^0.8.0;

interface IForwarder {
    function isForwardedTransaction() external view returns (bool)
}
```

### isForwardedTransaction

```javascript
function isForwardedTransaction() external view returns (bool)
```

**概要**

トランザクションがフォワーディングされたものか判定する関数。

**詳細**

受信者（**Recipient**）コントラクト内で呼び出され、トランザクションがフォワーディングされたものであるかどうかを確認します。
フォワーディングされたトランザクションは、通常のトランザクションとは異なり、特別な処理を受けることがあります。

**戻り値**

- `bool`
  - トランザクションがフォワーディングされた場合は「`true`」、それ以外の場合は「`false`」を返します。

:::note info
「**トランザクションがフォワーディングされた**」とは、トランザクションが通常の方法ではなく、特別な方法で処理されたことを指します。
トランザクションが受信者（**Recipient**）コントラクトによって処理されず、代わりに別のコントラクト（フォワーダー）によって処理された場合を指します。

フォワーディングされたトランザクションは、一般的には受信者コントラクトのコンテキストから別のスマートコントラクトに転送されたトランザクションを指します。
この転送は、通常は受信者コントラクトの外部から行われ、そのトランザクションを処理するフォワーダーが特定の操作を実行するために使用されます。

この関数が「`isForwardedTransaction`」を呼び出すことで、受信者コントラクトはトランザクションがフォワーディングされたものかどうかを判定します。
具体的な判定方法は、コントラクトの実装に依存しますが、通常はトランザクションの属性や特定のフラグを確認して、それがフォワーディングされたトランザクションであるかどうかを判断します。
これにより、受信者コントラクトはフォワーディングされたトランザクションに対して特別な処理を行うことができます。
:::

## SenderとForwarderの抽出

受信者（Recipient）の関数が呼び出される場合、受信者は呼び出し元の`isForwardedTransaction()`関数を`staticcall`で呼び出す必要があります。
この呼び出しに対する結果に応じて以下の処理が行われます。

1. もし`isForwardedTransaction()`が`revert`（処理を中断）するか、ブール値`false`を返す場合
   - トランザクションは通常通り処理されます。
   - 送信者（**Sender**）は呼び出し元です。
   - フォワーダー（**Forwarder**）は`0`アドレスに設定されます。

2. もし`isForwardedTransaction()`がブール値`true`を返す場合
   - トランザクションはフォワードされたトランザクションと見なされます。
   - 送信者は**ERC2771**が「トランザクションサインヤー」を抽出するために使用する手順を用いて抽出されます。
   - フォワーダーは呼び出し元に設定されます。


:::note info
受信者の関数呼び出し時に`isForwardedTransaction()`を確認し、その結果に応じてトランザクションの挙動と送信者・フォワーダーが決定されます。
これにより、メタトランザクションなどの特別なトランザクションを処理するための仕組みが実現されます。
:::

:::note info
**staticcall**

スマートコントラクト内で他のコントラクトの関数を読み取り専用で呼び出すためのEthereumのオペレーションの一つです。
以下に具体的な例を示します。

仮想通貨トークンの残高を取得するために「`staticcall`」を使用する場合を考えてみましょう。 
**ERC20**トークンは多くのスマートコントラクトで使用されるため、トークン残高を確認することは一般的な操作です。

例えば、あるスマートコントラクトAが**ERC20**トークンの残高を取得したい場合を考えます。

```solidity
// ERC-20トークンのアドレス
address public tokenAddress = 0x12345...; // トークンのアドレス

// トークンの残高を取得する関数
function getTokenBalance(address _owner) public view returns (uint256) {
    // ERC-20トークンのbalanceOf関数をstaticcallで呼び出す
    (bool success, bytes memory data) = tokenAddress.staticcall(abi.encodeWithSignature("balanceOf(address)", _owner));
    require(success, "staticcall failed");
    
    // 戻り値を取り出して返す
    return abi.decode(data, (uint256));
}
```

このコードでは、スマートコントラクトAは「`staticcall`」を使用して**ERC20**トークンの「`balanceOf`」関数を呼び出して、指定されたアドレス（`_owner`）の残高を取得しています。
ここでのポイントは、「`staticcall`」は読み取り専用の操作であるため、トークンの残高を取得するだけで、スマートコントラクトAの状態を変更しないことです。

このように「`staticcall`」を使用することで、他のスマートコントラクトの関数を呼び出して情報を取得することが可能で、安全性を確保しながら外部のデータを取得できます。
:::

:::note info
**ERC20**については以下を参考にしてください。
:::

https://chaldene.net/erc20

## 受信者（Recipient）コントラクトの拡張

もし受信者コントラクトが、関数のパラメータとしてアドレスを受け取る場合、以下のような新しい関数を追加することが求められます。
この新しい関数の名前は、元の関数と同じ名前ですが、末尾に「**Abstract**」という単語を追加します。
そして、この新しい関数は2つのアドレスを受け取ります。
1つ目のアドレスはフォワーダー（**Forwarder**）を指し、2つ目のアドレスはそのフォワーダーの制御下にあるアドレスを表します。

もし関数が複数のアドレスパラメータを受け取る場合（例：**ERC20**の`transferFrom`など）、各アドレスパラメータごとに2つのアドレスを受け取るこの「`Abstract`」関数を追加することで十分です。
そして、元の関数はフォワーダーアドレスをゼロアドレスに設定したオーバーロードされた関数と同じ結果を持つようになります。

具体的な例として、**ERC20**トークンは以下のような関数を拡張します。

```javascript
function transferAbstract(address toForwarder, address toAddress, uint256 amount);
function approveAbstract(address spenderForwarder, address spenderAddress, uint256 amount);
function transferFromAbstract(address fromForwarder, address fromAddress, address toForwarder, address toAddress, uint256 amount);
```

この拡張により、受信者コントラクトはトランザクションをカスタマイズするためにフォワーダーとその制御下のアドレスを指定でき、特定の操作を実行する際に柔軟性を持たせることができます。

# 補足

この提案は、EIP（Ethereum Improvement Proposal、イーサリアムの改善提案）に関する説明です。
EIPはイーサリアムプロトコルを改善し、新しい機能や規則を導入するための提案文書です。

この文の要点は、特定のEIPに対して、新しいアドレスパラメータを追加する選択肢について述べています。
新しいアドレスパラメータを追加する理由は「**このEIPの一般的な適用性を最大限に高めるため**」です。

:::note info
最も一般的に使用されるEIPに新しいアドレスパラメータを追加することで、このEIPを多くの場面で活用できることを意味しています。
新しい機能や仕組みを提供するEIPが、広く受け入れられるためには、既存のEIPとの互換性が重要です。
そのため、新しい機能を導入するために、既存のEIPに対して変更を加えるアプローチが採用されました。
:::

これにより、EIPの一般的な適用範囲が広がり、多くのスマートコントラクトやプロトコルで利用できるようになります。

# 後方互換性

既存のコントラクトはこのEIPを利用できません。
これは**ERC2771**にも当てはまります。

# セキュリティ考慮事項

受信者（Recipients）はフォワーダーに特別な信頼を置く必要はありません。
受信者自体はフォワーダーに対して信頼しなくても、通常のトランザクションを処理することができます。
しかし、実際にフォワーダーを使用するユーザーは、そのフォワーダーに対して信頼する必要があります。
なぜなら、ユーザーは自分のアカウントとフォワーダーを関連づけて利用するため、フォワーダーがユーザーのトランザクションを正確かつセキュアに処理することが重要だからです。

:::note info
受信者はフォワーダーに対して特別な信頼を置かなくても、フォワーダーを利用するユーザーはフォワーダーに対して信頼を築く必要があるというトレードオフが存在します。
これにより、受信者とフォワーダーの間での信頼関係が最小限に抑えられつつ、ユーザーはフォワーダーが安全にトランザクションを処理することを確信できるようになります。
:::

# 引用

Gavin John (@Pandapip1), "ERC-6315: ERC-2771 Account Abstraction [DRAFT]," Ethereum Improvement Proposals, no. 6315, January 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6315.

# 最後に

今回は「フォワーダーを信頼する必要のない仕組みを提案している、**ERC2771**の変種規格である**ERC6315**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58