[ERC4524] ERC20を安全に送付する仕組みを理解しよう！

# はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、**ERC20**の送付時に送付先がコントラクトの場合に、**ERC20**を取り扱うことができるか確認する仕組みを提案している**ERC4524**についてまとめていきます！

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

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

他にも様々なEIPについてまとめています。

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

# 概要

**ERC4524**は、従来の**ERC20**トークンに**EIP165**を組み込む提案をしています。
**EIP165**を導入することで、コントラクトがどのインターフェースを実装しているかを`supportsInterface`関数を呼び出すことで確認できるようになります。

また、NFT標準の**ERC721**やマルチトークン標準の**ERC1155**で用いられている、トークン受信時の検証や処理を行う関数群を**ERC20**に取り込みます。
これにより、トークンを受け取るコントラクトが正しく受信機能を実装していることを保証できます。

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

https://qiita.com/cardene/items/541184977a5cba51b759

# 動機

## 拡張機能の検出が難しい

**ERC20**はトークンの送受信だけを定義しており、トークン・パーミット（**ERC2612**など、一度のトランザクションで承認と送付を同時に行う仕組み）を実装しているかどうかをアプリや他のコントラクトが外部から判断する手段がありません。

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

https://qiita.com/cardene/items/6809173b2ac428b1ccd0

## モジュール設計の促進

**EIP165**の`supportsInterface`を活用すると、開発者やコントラクトは特定のEIPを実装しているかを動的に判別できます。
これにより、トークンごとに異なる機能対応を自動で分岐しやすくなり、柔軟で拡張性の高いプラットフォーム構築を支援します。

## 標準の一貫性維持

**ERC721**や**ERC1155**はすでに**EIP165**を取り入れており、NFTやマルチトークン領域で広く活用されています。
同様の仕組みを**ERC20**にも導入することで、今後の拡張標準との相互運用性が向上してコミュニティ全体での統一的な開発体験が実現します。

# 仕様

- **ERC20**トークンに**EIP165**（インターフェース検出機能）を組み込み、コントラクトがどのインターフェースを実装しているかを`supportsInterface`で判定できるようにする。  
- 受信コントラクトが正しくトークンを扱えるかを保証するため、**ERC721**や**ERC1155**で用いられる「`safeTransfer`」機能を**ERC20**にも導入する。

## 実装要件

- **ERC20**準拠のコントラクトは、**IERC20**と**IERC165**を継承し、以下のインターフェースを実装する。  
- このインターフェースの**EIP165**用`interfaceId`は`0x534f5876`である。

```js
pragma solidity 0.8.10;

import './IERC20.sol';
import './IERC165.sol';

// the EIP-165 interfaceId for this interface is 0x534f5876

interface SaferERC-20 is IERC20, IERC165 {
  function safeTransfer(address to, uint256 amount) external returns(bool);
  function safeTransfer(address to, uint256 amount, bytes memory data) external returns(bool);
  function safeTransferFrom(address from, address to, uint256 amount) external returns(bool);
  function safeTransferFrom(address from, address to, uint256 amount, bytes memory data) external returns(bool);
}
```

`safeTransfer`と`safeTransferFrom`は、以下の処理を実行できる必要があります。  

- EOA（外部所有アカウント）への送金はこれまで通り成功させる。  
- **ERC20Receiver**を実装し、`onERC20Received`呼び出しで返り値`0x4fc35859`を返すコントラクトへの送金は成功させる。  
- 受信コントラクトが**ERC20Receiver**を実装していない、あるいは正しいセレクタを返さない場合はロールバックする。

`safeTrransfer`を受け入れるコントラクトは、以下のインターフェースを実装し、`onERC20Received`が返り値`0x4fc35859`を返す必要があります。

```js
pragma solidity 0.8.10;

import './IERC165.sol';

interface ERC20Receiver is IERC165 {
  function onERC20Received(
    address _operator,
    address _from,
    uint256 _amount,
    bytes _data
  ) external returns(bytes4);
}
```

## 関数

### `safeTransfer`

```js
function safeTransfer(address to, uint256 amount) external returns(bool);
```

EOAアドレスおよび**ERC20Receiver**を実装し`onERC20Received`が`0x4fc35859`を返すコントラクトに対して、指定した`amount`分のトークンを送付する関数。
受信側が**ERC20Receiver**を実装していない、または正しいセレクタを返さない場合はロールバックします。

**引数**  
- `to`  
    - トークンを受け取るアドレス。  
- `amount`  
    - 送付するトークンの数量。

**戻り値**  
- `bool`  
    - 送付が成功した場合に`true`を返す。

### `safeTransfer`

```js
function safeTransfer(address to, uint256 amount, bytes memory data) external returns(bool);
```

第一引数`to`に指定したアドレスへ、`amount`分のトークンとともに`data`を渡して送付する関数。
EOAと**ERC20Receiver**実装コントラクトには成功し、未実装や誤った返り値の場合はロールバックします。

**引数**  
- `to`  
    - トークンを受け取るアドレス。  
- `amount`  
    - 送付するトークンの数量。  
- `data`  
    - 追加情報を含むバイト列。

**戻り値**  
- `bool`  
    - 送付が成功した場合に`true`を返す。

### `safeTransferFrom`

```js
function safeTransferFrom(address from, address to, uint256 amount) external returns(bool);
```

第三者の`from`アドレスから`to`アドレスへ、`amount`分のトークンを送付する関数。
EOAと**ERC20Receiver**実装コントラクトには成功し、未実装や誤った返り値の場合はロールバックします。

**引数**  
- `from`  
    - トークンを引き落とすアドレス。  
- `to`  
    - トークンを受け取るアドレス。  
- `amount`  
    - 送付するトークンの数量。

**戻り値**  
- `bool`  
    - 送付が成功した場合に`true`を返す。

### `safeTransferFrom`

```js
function safeTransferFrom(address from, address to, uint256 amount, bytes memory data) external returns(bool);
```

`from`から`to`へ、`amount`分のトークンと`data`を送付する関数。
EOAと**ERC20Receiver**実装コントラクトには成功し、未実装や誤った返り値の場合はロールバックします。

**引数**  
- `from`  
    - トークンを引き落とすアドレス。  
- `to`  
    - トークンを受け取るアドレス。  
- `amount`  
    - 送付するトークンの数量。  
- `data`  
    - 追加情報を含むバイト列。

**戻り値**  
- `bool`  
    - 送付が成功した場合に`true`を返す。

### `onERC20Received`

```js
function onERC20Received(
  address _operator,
  address _from,
  uint256 _amount,
  bytes _data
) external returns(bytes4);
```

**ERC20Receiver**インターフェースの一部として、`safeTransfer`や`safeTransferFrom`から呼び出される関数。呼び出し元コントラクトは返り値が`0x4fc35859`であることを確認し、これが正しくない場合は転送をロールバックします。

**引数**  
- `_operator`  
    - 送付を実行したコントラクトのアドレス。  
- `_from`  
    - 元のトークン所有者アドレス。  
- `_amount`  
    - 送付されたトークンの数量。  
- `_data`  
    - 送付時に渡された追加情報のバイト列。

**戻り値**  
- `bytes4`  
    - 成功を示すセレクタ`0x4fc35859`を返す。

# 補足

## 最小限の実装方針

**ERC4524**では、**ERC20**に**EIP165**を導入する時のコード増加をできるだけ抑えた設計を採用しています。
追加されるコード量は最小限のため、コントラクトサイズがわずかに増える以外に大きなデメリットはありません。

## `safeTransfer`系関数の命名と利点

`safeTransfer`および`safeTransferFrom`は、**ERC721**や**ERC1155**で既に広く使われている名前をそのまま踏襲しています。
この命名規則を維持することで、開発者は事前にコントラクトが安全にトークンを送付する機能をサポートしているかを簡単に確認でき、意図しないトークン消失リスクを低減できます。

## メタデータURIの未採用理由

**ERC721**や**ERC1155**ではトークン情報参照のためにメタデータURIが使われますが、**ERC4524**ではあえて含めていません。
理由は二つあります。
ひとつは「**最小限の実装**」にこだわるためであり、もうひとつはトークンメタデータURI機能が**ERC1046**で既に十分にカバーされているためです。
これにより重複する仕様を避け、シンプルさを維持しています。

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

https://qiita.com/cardene/items/90e8e7f6f1646f8c8cfe

# 互換性

互換性の問題はありません。

# セキュリティ

`onERC20Received`はコールバック関数です。
コールバック関数は悪用された事例があるため、実装が脆弱にならないよう注意が必要です。

# 引用

William Schwab (@wschwab), "ERC-4524: Safer ERC-20 [DRAFT]," Ethereum Improvement Proposals, no. 4524, December 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-4524.

# 最後に

今回は「**ERC20**の送付時に送付先がコントラクトの場合に、**ERC20**を取り扱うことができるか確認する仕組みを提案している**ERC4524**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

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