# [ERC1363] ERC20のtransferやapprove後に追加で関数を実行する仕組みを理解しよう！

> 現在、ERC20トークンのtransfer（送付）やapprove（承認）後にコードを実行する方法がないため、もし他の関数を実行したい場合は別のトランザクションを送信して2回のGASを支払う必要があります。

- URL: https://cardene.dev/articles/18e3a3e8104951b2d057
- 公開日: 2023-08-02
- 更新日: 2023-08-02
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC20**トークンの`transfer`（送付）や`approve`（承認）後に、追加で送付先や承認先のコントラクト内の関数を実行する**ERC1363**についてまとめていきます！

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

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

この提案は、**ERC20**トークンのインターフェースを定義し、トランザクションの実行後に別の処理を行うコードを実行できるようにするものです。
具体的には、トークンの`transfer`（送付）や`approve`（承認）後に、別の処理を行うコードを自動的に呼び出す方法を提供します。

![erc1363.png](/images/qiita-a67f49dc-4f4c-203e-dcb4-b80d34b9b7db.webp)

この提案では、以下の新しい関数を導入します。

### transferAndCallとtransferFromAndCall
`ERC1363Receiver`コントラクトの`onTransferReceived`関数を呼び出します。
これにより、`transfer`（送付）や`transferFrom`（承認された送付）を実行した後に、追加の処理を実行することができます。

![transfer.png](/images/qiita-00881555-7220-5c26-a3ae-4a9b642c13e5.webp)

### approveAndCall
`ERC1363Spender`コントラクトの`onApprovalReceived`関数を呼び出します。
これにより、`approve`（承認）を実行した後に追加の処理を実行することができます。

![approve.png](/images/qiita-950cd3a5-4f7d-bc5a-c1c5-cc0f540fa838.webp)

:::message
**Spender**とは、トークンの`transfer`（送付）の承認をされたアドレスのことを指します。
:::

## 動機

現在、**ERC20**トークンの`transfer`（送付）や`approve`（承認）後にコードを実行する方法がないため、もし他の関数を実行したい場合は別のトランザクションを送信して2回のGASを支払う必要があります。

この提案は、トークン支払いをより簡単に行い、別のリスナーを使用せずにトランザクション後にコードを実行できるようにすることを目的としています。

例として、以下のようなEthereumスマートコントラクトを想定しています。

### ICO
クラウドセールの略で、新しい暗号通貨（トークン）を発行して、それを投資家や一般のユーザーに対して販売する資金調達手法の1つのことです。
購入者がトークンを`transfer`（送付）してからトークンの配布を行うとします。
**ERC1363**を使用することで、投資家がトークンを`transfer`（送付）した後に、自動的にクラウドセールのコントラクト内の関数が実行され、送られたトークン量に応じて購入者にクラウドセールトークンを配布することができます。

### サービス提供
サービス提供者がトークンを受け取ることでサービスを提供する場合、**ERC1363**を使用してサービス提供の確認およびトークンの受け取り処理を自動化することができます。
顧客がトークンを`transfer`（送付）したら、トークンの数量に応じたサービス提供を開始する処理が可能です。

### 請求書支払い
請求書をトークンで支払う場合、**ERC1363**を使用して請求書の支払い処理を自動化することができます。
請求書を発行した企業が支払われたトークンの数量に応じて請求書の支払いを確認することができます。

### サブスクリプション

定期的な支払いをトークンで行いたい場合、、**ERC1363**を使用して定期的な支払いの処理を自動化することができます。
トークンを`transfer`（送付）された時に、サブスクリプションを有効化しサービスの提供が開始されます。
その後、特定の期間ごとにトークンを受け取り、対応するサービスの提供を継続します。
もし定期的な支払いに必要なトークンが不足している場合、支払いが失敗し通知することができます。

この提案は**ERC721**の**onERC721Received**と**ERC721TokenReceiver**の仕組みに着想を得ています。

## 仕様

### ERC1363

```javascript
pragma solidity ^0.8.0;

interface ERC1363 /* is ERC20, ERC165 */ {
  function transferAndCall(address to, uint256 value) external returns (bool);

  function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool);

  function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

  function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool);

  function approveAndCall(address spender, uint256 value) external returns (bool);

  function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool);
}

interface ERC20 {
  function totalSupply() external view returns (uint256);
  function balanceOf(address account) external view returns (uint256);
  function transfer(address recipient, uint256 amount) external returns (bool);
  function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
  function allowance(address owner, address spender) external view returns (uint256);
  function approve(address spender, uint256 amount) external returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
  event Approval(address indexed owner, address indexed spender, uint256 value);
}

interface ERC165 {
  function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
```

#### transferAndCall

トークンを`transfer`（送付）した後、トークンの受け取りコントラクト内の`onTransferReceived`関数を呼び出す関数。
受け取りコントラクトに追加のデータを送信もできます。

#### transferFromAndCall

他のアドレスからトークンを受け取り、トークンの受け取りコントラクト内の`onTransferReceived`関数を呼び出す関数。
受け取りコントラクトに追加のデータを送信もできます。

#### approveAndCall

他のアドレスに対してトークンの`approve`（承認）を行い対応する`Spender`コントラクト内の`onApprovalReceived`関数を呼び出す関数。
受け取りコントラクトに追加のデータを送信もできます。

:::message
**ERC20**については以下を参照してください。
:::

https://chaldene.net/erc20

:::message
**ERC165**については以下を参照してください。
:::

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

### ERC1363Receiver

```javascript
interface ERC1363Receiver {
  function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4);
}
```

`ERC1363Receiver`インターフェースの識別子は`0x88a7ca5c`です。

#### onTransferReceived

**ERC1363**のトークンコントラクトから`transfer`または`transferFrom`関数が実行された後に、受け取りコントラクト内で呼び出される関数。
受け取りコントラクトは、この関数を実装することで、トークンの受け取り時に特定の処理を実行することができます。
戻り値として、`bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`を返す。
それ以外の値を返すとトランザクションがリバート（巻き戻し）されます。
これにより、トランザクションの正常性を確認することができます。

- `operator`
    - `transferAndCall`または`transferFromAndCall`関数を呼び出したアドレス（トークンの送信元または承認元）。
- `from`
    - トークンが送信されたアドレス（`transfer`または`transferFrom`の送信元）。
- `value`
    - トークンの数量（送信されたトークンの量）。
- `data`
    - 追加のデータ（特定のフォーマットが指定されていない追加の情報）。


### ERC1363Spender

```javascript
interface ERC1363Spender {
  function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4);
}
```

`ERC1363Spender`インターフェースの識別子は`0x7b04a2d0`です。

#### onApprovalReceived
**ERC1363**のトークンコントラクトから`approve`関数が実行された後に、**Spender**のコントラクト内で呼び出される関数です。
**Spender**のコントラクトは、この関数を実装することでトークンの承認時に特定の処理を実行することができます。
戻り値として、`bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`を返す。
それ以外の値を返すとトランザクションがリバート（巻き戻し）されます。
これにより、トランザクションの正常性を確認することができます。

- `owner`
    - `approveAndCall`関数を呼び出したアドレス（トークンの所有者）。
- `value`
    - 承認されたトークンの数量（承認されたトークンの量）。
- `data`
    - 追加のデータ（特定のフォーマットが指定されていない追加の情報）。

## 補足

`transferAndCall`、`transferFromAndCall`、`approveAndCall`の関数名は、**ERC20**トークンの関数名から派生しています。
これらの関数は、`transfer`、`transferFrom`、および`approve`と同じ振る舞いをしながら、受け取りコントラクトや**Spender**に対してコールバックを追加したものです。

:::message
コールバックとは、ある処理が完了した後に自動的に実行される関数のことを指します。
:::

つまり、トークンの`transfer`（送付）や`approve`（承認）が行われた後に、受け取りコントラクトや**Spender**のコントラクトで特定の処理を実行することができます。

## 互換性

この提案は、**ERC223**や**ERC677**からのインスピレーションを受けていますが、**ERC20**の手法を採用しています。
つまり、**ERC20**の`transfer`および`transferFrom`メソッドを上書きせず、**ERC20**の後方互換性を保ちながら、新たな機能を追加しています。
これにより、既存の**ERC20**トークンの実装を変更することなく、新しい機能を導入できます。

## セキュリティ上の考慮

`approveAndCall`および`transferFromAndCall`メソッドは、標準の**ERC20**の`approve`および`transferFrom`関数と同じ問題に影響を受ける可能性があります。
`approveAndCall`メソッドで`transfer`（送付）許可を変更する際に、トランザクションの順序によって古い許可と新しい許可の両方が使用される危険性があります。
この競合条件を緩和するための解決策は、まず**Spender**の許可を`false`に変更し、その後に目的の値を設定する方法があります。

## コード

コードは以下のコードをそのまま使用しています。

https://github.com/vittominacori/erc1363-payable-token/tree/master/contracts/token/ERC1363

以下が自分のリポジトリです。

https://github.com/cardene777/base-code/tree/develop/contract/contracts/ERC1363

## テスト

以下がテストコードです。

https://github.com/cardene777/base-code/blob/develop/contract/test/ERC1363/ERC1363.test.ts

## 考察

トークンの`transfer`（送付）や`approve`（承認）したときに、追加でトランザクションを走らせずに処理を実行できるの便利です。
可能性はだいぶ広がりますが、実装の難易度は高そうな印象を受けました。
また、トークンの`transfer`（送付）や`approve`（承認）先はコントラクトである必要があるため、コントラクトアドレスか確認することが重要です。
サブスクリプションを実装できるのは個人的に面白そうだと思ったので、何かしら作ってみたいと思いました。
ぜひ皆さんも色々とアイデアを出してみてください！


## 引用

Vittorio Minacori (@vittominacori), "ERC-1363: Payable Token," Ethereum Improvement Proposals, no. 1363, January 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1363.

## 最後に

今回は「ERC721とERC20の機能を併せ持つ半代替性トークンと呼ばれている**ERC3525**」についてまとめてきました！
いかがだったでしょうか？

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

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

## 採用強化中！

CryptoGamesでは一緒に働く仲間を大募集中です。

https://www.wantedly.com/companies/cryptogames

この記事で書いた自分の経験からもわかるように、裁量権を持って働くことができて一気に成長できる環境です。
「ブロックチェーンやWeb3、NFTに興味がある」、「スマートコントラクトの開発に携わりたい」など、少しでも興味を持っている方はまずはお話ししましょう！
