# [ERC5507] ERC20、ERC721、ERC1155の払い戻し機能の仕組みを理解しよう！

> このERCの提案は、ERC20、ERC721、ERC1155トークンの新規販売において、返金が可能な機能を追加するものです。

- URL: https://cardene.dev/articles/cb67afec0453715158b9
- 公開日: 2023-11-13
- 更新日: 2023-11-13
- トピック: Blockchain
- タグ: ethereum, solidity, ブロックチェーン, nft

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC20**、**ERC721**、**ERC1155**トークンに払い戻し機能の追加を提案している規格である**ERC5507**についてまとめていきます！

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

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

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

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

## 概要

このERCの提案は、**ERC20**、**ERC721**、**ERC1155**トークンの新規販売において、返金が可能な機能を追加するものです。

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

https://chaldene.net/erc20

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

https://chaldene.net/erc721

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

https://chaldene.net/erc1155

この機能により、ユーザーがトークンを購入したお金は、事前に決定された期間が終了するまで、第三者が管理するエスクロー口座に保管されます。
この期間中であれば、ユーザーは購入したトークンを返して、支払ったお金を戻してもらうことができます。

:::message
例えば、もしトークンを買ったけれどもプロジェクトが思ったように進まなかった場合、ユーザーは`refund`関数を通じてトークンを返却し、投資した資金を取り戻すことができます。
:::

## 動機

NFTとトークンの世界では開発者が資金を持ち逃げする「**rugpull**」という問題があり、これを防ぐための信頼できる方法が必要です。
購入者への返金機能を導入することで、購入者を保護し、作成者の信頼性も向上します。

このような場合に使う標準化されたインターフェースには、以下のようなメリットがあります。

- EUの「**Distance Selling Regulation**」に適合しやすくなり、オンラインで購入された商品（例えばトークン）に14日間の返金期間が必要です。

:::message
**Distance Selling Regulation**
主にヨーロッパ連合（EU）において適用される消費者保護の法規則です。
この規則は、消費者が店舗外（オンラインショップ、郵便注文、電話販売など）で商品やサービスを購入する時に適用され、消費者に返品やキャンセルの権利を保証しています。

特に、EUでは「**遠隔販売規制**」として知られており、オンラインで商品やサービスを購入した消費者には、商品到着後14日以内に無条件でキャンセルする権利が与えられています。
これには、理由を問わずに購入した商品を返送し、全額返金を受けることが含まれます。
この規則の目的は、消費者が実際に商品を見ることなく購入を決定する遠隔販売の特性によるリスクを軽減することにあります。

「**Distance Selling Regulatios**」は、遠隔販売における透明性を高め、消費者が信頼して取引できるようにするために、販売者が守るべき一連の義務も定めています。
例えば、販売者は商品や契約条件、キャンセルポリシーなどに関する明確な情報を消費者に提供しなければなりません。
:::

- ポートフォリオブラウザーやマーケットプレイスといったNFT関連アプリとの相互運用が可能になります。
- NFTマーケットプレイスは、NFTがまだ返金可能であることを示すバッジを商品に表示したり、マーケットプレイスでのリストよりも返金を提案することができます。
- 分散型取引所は、返金によってより高い収益が見込める場合、トークンの返金を提供できます。
- ウォレットはトークンが返金されるアクションをユーザーにより明確に伝えることができるようになります。
    - これは、トークンの転送時に特有のダイアログが表示されるのと似ています。
- DAOは、トークンを返金する機能を含む提案をより明確に表示することができます。

これらの機能は、NFTやトークンの購入者をより守り、市場の信頼性を高めるために大切なステップです。
コントラクトや他のツールがこの新しい標準を採用することで、より安全で信頼性の高い取引環境が作り出されることが期待されます。

## 仕様

この規格に準拠する実装は**ERC165**も実装する必要があります。

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

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

### ERC20 Refund Extension

```javascript
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.17;

import "ERC20.sol";
import "ERC165.sol";

/// @notice Refundable ERC-20 tokens
/// @dev    The ERC-165 identifier of this interface is `0xf0ca2917`
interface ERC20Refund is ERC20, ERC165 {
    /// @notice           Emitted when a token is refunded
    /// @dev              Emitted by `refund`
    /// @param  _from     The account whose assets are refunded
    /// @param  _amount   The amount of token (in terms of the indivisible unit) that was refunded
    event Refund(
        address indexed _from,
        uint256 indexed _amount
    );

    /// @notice           Emitted when a token is refunded
    /// @dev              Emitted by `refundFrom`
    /// @param  _sender   The account that sent the refund
    /// @param  _from     The account whose assets are refunded
    /// @param  _amount   The amount of token (in terms of the indivisible unit) that was refunded
    event RefundFrom(
        address indexed _sender,
        address indexed _from,
        uint256 indexed _amount
    );

    /// @notice         As long as the refund is active, refunds the user
    /// @dev            Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
    /// @param  amount  The `amount` to refund
    function refund(uint256 amount) external;

    /// @notice         As long as the refund is active and the sender has sufficient approval, refund the tokens and send the ether to the sender
    /// @dev            Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
    ///                 The ether goes to msg.sender.
    /// @param  from    The user from which to refund the assets
    /// @param  amount  The `amount` to refund
    function refundFrom(address from, uint256 amount) external;

    /// @notice         Gets the refund price
    /// @return _wei    The amount of ether (in wei) that would be refunded for a single token unit (10**decimals indivisible units)
    function refundOf() external view returns (uint256 _wei);
 
    /// @notice         Gets the first block for which the refund is not active
    /// @return block   The first block where the token cannot be refunded
    function refundDeadlineOf() external view returns (uint256 block);
}
```

---

#### `Refund`

```javascript
event Refund(
    address indexed _from,
    uint256 indexed _amount
);
```

**概要**

トークンが返金された時に発行されるイベント。

**詳細**

このイベントは、`refund`関数によってトリガーされます。
トークンの返金が行われると、このイベントが発行され、返金されたアカウントと金額の情報がブロックチェーンに記録されます。

**パラメータ**

- `_from`
    - 資産が返金されたアカウント。
- `_amount`
    - 返金されたトークンの量（最小分割単位で）。

---

#### `RefundFrom`

```javascript
event RefundFrom(
    address indexed _sender,
    address indexed _from,
    uint256 indexed _amount
);
```

**概要**

他のアカウントに代わってトークンが返金された時に発行されるイベント。

**詳細**

このイベントは、`refundFrom`関数によってトリガーされます。
誰かが他人のアカウントからトークンを返金するときにこのイベントが発行され、実行者、返金されたアカウント、そして返金された金額の情報がブロックチェーンに記録されます。

**パラメータ**

- `_sender`
    - 返金を送ったアカウント。
- `_from`
    - 資産が返金されたアカウント。
- `_amount`
    - 返金されたトークンの量（最小分割単位で）。

---

#### `refund`

```javascript
function refund(uint256 amount) external;
```

**概要**

返金がアクティブである限り、ユーザーに対して返金を行う関数。

**詳細**

この関数を実行する前に、ユーザーがトークンを保持していることを確認し、再入可能性に関する問題がないか注意深くチェックする必要があります。

**引数**

- `amount`
    - 返金する金額。

---

#### `refundFrom`

```javascript
function refundFrom(address from, uint256 amount) external;
```

**概要**

返金がアクティブであり、送信者に十分な承認がある場合にトークンを返金し**ETH**を送信者に送る関数。

**詳細**

ユーザーがトークンを保持していることを確認し、再入可能性に関する問題に注意しながら**ETH**を`msg.sender`に送信します。

**引数**

- `from`
    - 資産を返金するユーザー。
- `amount`
    - 返金する金額。

---

#### `refundOf`

```javascript
function refundOf() external view returns (uint256 _wei);
```

**概要**

返金価格を取得する関数。

**詳細**

一つのトークン単位（10**decimalsで表される分割不可能な単位）に対して返金されるイーサの量（`wei`単位）を返します。

**戻り値**

- `_wei`
    - 単一のトークン単位に対する返金額（`wei`単位）。

---

#### `refundDeadlineOf`

```javascript
function refundDeadlineOf() external view returns (uint256 block);
```

**概要**

返金がアクティブでない最初のブロックを取得する関数。

**詳細**

トークンが返金できない最初のブロック番号を返します。

**戻り値**

- `block`
    - トークンが返金できない最初のブロック番号。

---

### ERC721 Refund Extension

```javascript
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.17;

import "ERC721.sol";
import "ERC165.sol";

/// @notice Refundable ERC-721 tokens
/// @dev    The ERC-165 identifier of this interface is `0xe97f3c83`
interface ERC721Refund is ERC721 /* , ERC165 */ {
    /// @notice           Emitted when a token is refunded
    /// @dev              Emitted by `refund`
    /// @param  _from     The account whose assets are refunded
    /// @param  _tokenId  The `tokenId` that was refunded
    event Refund(
        address indexed _from,
        uint256 indexed _tokenId
    );

    /// @notice           Emitted when a token is refunded
    /// @dev              Emitted by `refundFrom`
    /// @param  _sender   The account that sent the refund
    /// @param  _from     The account whose assets are refunded
    /// @param  _tokenId  The `tokenId` that was refunded
    event RefundFrom(
        address indexed _sender,
        address indexed _from,
        uint256 indexed _tokenId
    );

    /// @notice         As long as the refund is active for the given `tokenId`, refunds the user
    /// @dev            Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
    /// @param  tokenId The `tokenId` to refund
    function refund(uint256 tokenId) external;

    /// @notice         As long as the refund is active and the sender has sufficient approval, refund the token and send the ether to the sender
    /// @dev            Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
    ///                 The ether goes to msg.sender.
    /// @param  from    The user from which to refund the token
    /// @param  tokenId The `tokenId` to refund
    function refundFrom(address from, uint256 tokenId) external;

    /// @notice         Gets the refund price of the specific `tokenId`
    /// @param  tokenId The `tokenId` to query
    /// @return _wei    The amount of ether (in wei) that would be refunded
    function refundOf(uint256 tokenId) external view returns (uint256 _wei);
 
    /// @notice         Gets the first block for which the refund is not active for a given `tokenId`
    /// @param  tokenId The `tokenId` to query
    /// @return block   The first block where token cannot be refunded
    function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block);
}
```

---

#### `Refund`

```javascript
event Refund(
    address indexed _from,
    uint256 indexed _tokenId
);
```

**概要**

トークンが返金された時に発行されるイベント。

**詳細**

このイベントは`refund`関数によって発行され、トークンの返金が行われたときにブロックチェーンに記録されます。

**パラメータ**

- `_from`
    - 資産が返金されたアカウント。
- `_tokenId`
    - 返金されたトークンのID。

---

#### `RefundFrom`

```javascript
event RefundFrom(
    address indexed _sender,
    address indexed _from,
    uint256 indexed _tokenId
);
```

**概要**

代わりにトークンを返金した時に発行されるイベント。

**詳細**

このイベントは`refundFrom`関数によって発行され、他のアカウントに代わってトークンの返金が行われたときにブロックチェーンに記録されます。

**パラメータ**

- `_sender`
    - 返金を行ったアカウント。
- `_from`
    - 資産が返金されたアカウント。
- `_tokenId`
    - 返金されたトークンのID。

---

#### `refund`

```javascript
function refund(uint256 tokenId) external;
```

**概要**

指定された`tokenId`について、返金がアクティブである限りユーザーに返金を行う関数。

**詳細**

この関数を実行する前に、ユーザーがそのトークンを保持していること、そして再入可能性の問題がないかを確認する必要があります。

**引数**

- `tokenId`
    - 返金するトークンのID。

---

#### `refundFrom`

```javascript
function refundFrom(address from, uint256 tokenId) external;
```

**概要**

返金がアクティブであり送信者に十分な承認がある場合、トークンを返金し**ETH**を送信者に送る関数。

**詳細**

ユーザーがトークンを保持していることを確認し、再入可能性の問題に注意しながら**ETH**を`msg.sender`に送る必要があります。

**引数**

- `from`
    - トークンを返金するユーザー。
- `tokenId`
    - 返金するトークンのID。

---

#### `refundOf`

```javascript
function refundOf(uint256 tokenId) external view returns (uint256 _wei);
```

**概要**

特定の`tokenId`の返金価格を取得する関数。

**詳細**

指定されたトークンIDに対して返金される**ETH**の量を`wei`単位で取得します。

**引数**

- `tokenId`
    - 問い合わせるトークンのID。

**戻り値**

- `_wei`
    - 返金されるイーサの量（`wei`単位）。

---

#### `refundDeadlineOf`

```javascript
function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block);
```

**概要**

指定された`tokenId`に対して返金がアクティブでない最初のブロックを取得する関数。

**詳細**

トークンIDに基づいて、返金が可能でなくなる最初のブロック番号を取得します。

**引数**

- `tokenId`
    - 問い合わせるトークンのID。

**戻り値**

- `block`
    - トークンが返金できなくなる最初のブロック番号。

---

### ERC721 Batch Refund Extension (Option)

```javascript
// SPDX-License-Identifier: CC0-1.0;

import "ERC721Refund.sol";

/// @notice Batch Refundable ERC-721 tokens
/// @dev    The ERC-165 identifier of this interface is ``
contract ERC721BatchRefund is ERC721Refund {
    /// @notice           Emitted when one or more tokens are batch refunded
    /// @dev              Emitted by `refundBatch`
    /// @param  _from     The account whose assets are refunded
    /// @param  _tokenId  The `tokenIds` that were refunded
    event RefundBatch(
        address indexed _from,
        uint256[] _tokenIds // This may or may not be indexed
    );

    /// @notice           Emitted when one or more tokens are batch refunded
    /// @dev              Emitted by `refundFromBatch`
    /// @param  _sender   The account that sent the refund
    /// @param  _from     The account whose assets are refunded
    /// @param  _tokenId  The `tokenId` that was refunded
    event RefundFromBatch(
        address indexed _sender,
        address indexed _from,
        uint256 indexed _tokenId
    );
    
    /// @notice           As long as the refund is active for the given `tokenIds`, refunds the user
    /// @dev              Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors
    ///                   These must either succeed or fail together; there are no partial refunds.
    /// @param  tokenIds  The `tokenId`s to refund
    function refundBatch(uint256[] tokenIds) external;

    /// @notice           As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender
    /// @dev              Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors
    ///                   The ether goes to msg.sender.
    ///                   These must either succeed or fail together; there are no partial refunds.
    /// @param  from      The user from which to refund the token
    /// @param  tokenIds  The `tokenId`s to refund
    function refundFromBatch(address from, uint256[] tokenIds) external;
}
```

---

#### `RefundBatch`

```javascript
event RefundBatch(
    address indexed _from,
    uint256[] _tokenIds
);
```

**概要**

一つまたは複数のトークンが一括で返金された時に発行されるイベント。

**詳細**

このイベントは`refundBatch`関数によって発行され、複数のトークンが一括で返金された際にブロックチェーンに記録されます。

**パラメータ**

- `_from`
    - 資産が返金されたアカウント。
- `_tokenIds`
    - 返金されたトークンIDの配列。

---

#### `RefundFromBatch`

```javascript
event RefundFromBatch(
    address indexed _sender,
    address indexed _from,
    uint256 indexed _tokenId
);
```

**概要**

一つまたは複数のトークンが他のアカウントに代わって一括で返金された時に発行されるイベント。

**詳細**

このイベントは`refundFromBatch`関数によって発行され、別のアカウントが代表して複数のトークンを一括で返金した際にブロックチェーンに記録されます。

**パラメータ**

- `_sender`
    - 返金を送ったアカウント。
- `_from`
    - 資産が返金されたアカウント。
- `_tokenId`
    - 返金されたトークンID。
    - このパラメータはインデックス付きです。

---

#### `refundBatch`

```javascript
function refundBatch(uint256[] tokenIds) external;
```

**概要**

指定された`tokenIds`について、返金がアクティブである限りユーザーに一括返金を行う関数。

**詳細**

ユーザーが指定されたトークンを所有しているかを確認し、再入可能性の問題がないかを注意してください。
この操作は、全てのトークンの返金が成功するか、または失敗するかのどちらかであり、部分的な返金はありません。

**引数**

- `tokenIds`
    - 返金するトークンIDの配列。

---

#### `refundFromBatch`

```javascript
function refundFromBatch(address from, uint256[] tokenIds) external;
```

**概要**

指定された`tokenIds`について、返金がアクティブであり、かつ送信者に十分な承認がある場合、トークンを一括返金し**ETH**を送信者に送る関数。

**詳細**

ユーザーが指定されたトークンを所有しているかを確認し、再入可能性の問題がないかを注意してください。
イーサは`msg.sender`に送られます。
この操作は、全てのトークンの返金が成功するか失敗するかのどちらかであり、部分的な返金はありません。

**引数**

- `from`
    - トークンを返金するユーザー。
- `tokenIds`
    - 返金するトークンIDの配列。

---

### ERC1155 Refund Extension

#### `Refund`

```javascript
event Refund(
    address indexed _from,
    uint256 indexed _tokenId,
    uint256 _amount
);
```

**概要**

トークンが返金された時に発行されるイベント。

**詳細**

`refund`関数によってこのイベントが発行され、返金をリクエストしたアカウント、返金されたトークンのID、および返金されたトークンの量に関する情報がブロックチェーンに記録されます。

**パラメータ**

- `_from`
    - 返金をリクエストしたアカウント。
- `_tokenId`
    - 返金されたトークンのID。
- `_amount`
    - 返金されたトークンの量。

---

#### `RefundFrom`

```javascript
event RefundFrom(
    address indexed _sender,
    address indexed _from,
    uint256 indexed _tokenId
);
```

**概要**

他のアカウントに代わってトークンが返金された時に発行されるイベント。

**詳細**

`refundFrom`関数によってこのイベントが発行され、返金を送ったアカウント、資産が返金されたアカウント、返金されたトークンのIDに関する情報がブロックチェーンに記録されます。
ただし、このイベント定義では、返金されたトークンの量（`_amount`）が含まれていない点に注意してください。

**パラメータ**

- `_sender`
    - 返金を送ったアカウント。
- `_from`
    - 資産が返金されたアカウント。
- `_tokenId`
    - 返金されたトークンのID。

---

#### `refund`

```javascript
function refund(uint256 tokenId, uint256 amount) external;
```

**概要**

指定された`tokenId`に対して返金がアクティブである限り、ユーザーに返金を行う関数。

**詳細**

ユーザーが十分なトークンを持っていること、および再入可能性の問題がないことを確認してください。
返金処理は、指定されたトークンIDと返金する量に基づいて行われます。

**引数**

- `tokenId`
    - 返金するトークンのID。
- `amount`
    - 返金するトークンの量。

---

#### `refundFrom`

```javascript
function refundFrom(address from, uint256 tokenId, uint256 amount) external;
```

**概要**

返金がアクティブであり、送信者に十分な承認がある限りトークンを返金し**ETH**を送信者に送る関数。

**詳細**

ユーザーが十分なトークンを持っていることを確認し、再入可能性の問題に注意してください。
**ETH**は`msg.sender`に送られます。
返金処理は、トークンを返金するユーザー、指定されたトークンID、返金する量に基づいて行われます。

**引数**

- `from`
    - トークンを返金するユーザー。
- `tokenId`
    - 返金するトークンのID。
- `amount`
    - 返金するトークンの量。

---

#### `refundOf`

```javascript
function refundOf(uint256 tokenId) external view returns (uint256 _wei);
```

**概要**

特定の`tokenId`の返金価格を取得する関数。

**詳細**

指定されたトークンIDの一つあたりの返金される**ETH**の量（`wei`単位）を返します。

**引数**

- `tokenId`
    - 問い合わせるトークンのID。

**戻り値**

- `_wei`
    - 単一のトークンに対する返金額（`wei`単位）。

---

#### `refundDeadlineOf`

```javascript
function refundDeadlineOf(uint256 tokenId) external view returns (uint256 block);
```

**概要**

指定された`tokenId`に対して返金がアクティブでない最初のブロックを取得する関数。

**詳細**

トークンIDに基づいて、返金が可能でなくなる最初のブロック番号を取得します。

**引数**

- `tokenId`
    - 問い合わせるトークンのID。

**戻り値**

- `block`
    - トークンが返金できなくなる最初のブロック番号。

---


### ERC1155 Batch Refund Extension (Option)

```javascript
// SPDX-License-Identifier: CC0-1.0;

import "ERC1155Refund.sol";

/// @notice Batch Refundable ERC-1155 tokens
/// @dev    The ERC-165 identifier of this interface is ``
contract ERC1155BatchRefund is ERC1155Refund {
    /// @notice           Emitted when one or more tokens are batch refunded
    /// @dev              Emitted by `refundBatch`
    /// @param  _from     The account that requested a refund
    /// @param  _tokenIds The `tokenIds` that were refunded
    /// @param  _amounts  The amount of each `tokenId` that was refunded
    event RefundBatch(
        address indexed _from,
        uint256[] _tokenIds, // This may or may not be indexed
        uint256[] _amounts
    );

    /// @notice           Emitted when one or more tokens are batch refunded
    /// @dev              Emitted by `refundFromBatch`
    /// @param  _sender   The account that sent the refund
    /// @param  _from     The account whose assets are refunded
    /// @param  _tokenIds The `tokenIds` that was refunded
    /// @param  _amounts  The amount of each `tokenId` that was refunded
    event RefundFromBatch(
        address indexed _sender,
        address indexed _from,
        uint256[] _tokenId, // This may or may not be indexed
        uint256[] _amounts
    );
    
    /// @notice           As long as the refund is active for the given `tokenIds`, refunds the user
    /// @dev              Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors
    ///                   These must either succeed or fail together; there are no partial refunds.
    /// @param  tokenIds  The `tokenId`s to refund
    /// @param  amounts   The amount of each `tokenId` to refund
    function refundBatch(uint256[] tokenIds, uint256[] amounts) external;

    /// @notice           As long as the refund is active for the given `tokenIds` and the sender has sufficient approval, refund the tokens and send the ether to the sender
    /// @dev              Make sure to check that the user has the tokens, and be aware of potential re-entrancy vectors
    ///                   The ether goes to msg.sender.
    ///                   These must either succeed or fail together; there are no partial refunds.
    /// @param  from      The user from which to refund the token
    /// @param  tokenIds  The `tokenId`s to refund
    /// @param  amounts   The amount of each `tokenId` to refund
    function refundFromBatch(address from, uint256[] tokenIds, uint256[] amounts external;
}
```

---

#### `RefundBatch`

```javascript
event RefundBatch(
    address indexed _from,
    uint256[] _tokenIds,
    uint256[] _amounts
);
```

**概要**

複数のトークンが一括で返金された時に発行されるイベント。

**詳細**

このイベントは`refundBatch`関数によって発行され、返金を要求したアカウントと返金された各トークンIDとその量に関する情報がブロックチェーンに記録されます。

**パラメータ**

- `_from`
    - 返金を要求したアカウント。
- `_tokenIds`
    - 返金されたトークンIDの配列。
- `_amounts`
    - 各`tokenId`に返金された量の配列。

---

#### `RefundFromBatch`

```javascript
event RefundFromBatch(
    address indexed _sender,
    address indexed _from,
    uint256[] _tokenIds,
    uint256[] _amounts
);
```

**概要**

他のアカウントに代わって複数のトークンが一括で返金された時に発行されるイベント。

**詳細**

このイベントは`refundFromBatch`関数によって発行され、返金を送ったアカウントと資産が返金されたアカウント、返金されたトークンIDとその量に関する情報がブロックチェーンに記録されます。

**パラメータ**

- `_sender`
    - 返金を送ったアカウント。
- `_from`
    - 資産が返金されたアカウント。
- `_tokenIds`
    - 返金されたトークンIDの配列。
    - この配列はインデックス付けされているかもしれません。
- `_amounts`
    - 各`tokenId`に返金された量の配列。

---

#### `refundBatch`

```javascript
function refundBatch(uint256[] tokenIds, uint256[] amounts) external;
```

**概要**

指定された`tokenIds`について返金がアクティブである限り、ユーザーにそれぞれの`tokenId`の指定された量を一括返金する関数。

**詳細**

ユーザーが十分な量のトークンを持っているかを確認し、再入可能性のリスクに注意する必要があります。
全ての返金処理は同時に成功するか、失敗するかのどちらかであり、部分的な返金は行われません。

**引数**

- `tokenIds`
    - 返金するトークンのIDの配列。
- `amounts`
    - 各`tokenId`に返金する量の配列。

---

#### `refundFromBatch`

```javascript
function refundFromBatch(address from, uint256[] tokenIds, uint256[] amounts) external;
```

**概要**

指定された`tokenIds`について返金がアクティブであり、送信者に十分な承認がある場合トークンを一括返金し**ETH**を送信者に送る関数。

**詳細**

ユーザーが十分な量のトークンを持っているかを確認し、再入可能性のリスクに注意する必要があります。
**ETH**は`msg.sender`に送られます。
全ての返金処理は同時に成功するか、失敗するかのどちらかであり、部分的な返金は行われません。

**引数**

- `from`
    - トークンを返金するユーザー。
- `tokenIds`
    - 返金するトークンのIDの配列。
- `amounts`
    - 各`tokenId`に返金する量の配列。

---

## 補足

`refundDeadlineOf`関数は、返金可能な期限をブロック番号で設定しています。
これは、タイムスタンプよりもブロック番号の方が信頼性が高いためです。

`refund`、`refundOf`、`refundDeadlineOf`という関数名は、**ERC20**、**ERC721**、**ERC1155**といった既存のコントラクト規格に合わせて名付けられています。

**ERC165**は、DAppsがコントラクトとやり取りする際の内省を容易にするために必要です。
これがないと、DAppsはコントラクトが特定のインターフェースをサポートしているかどうかを効率的に確認できません。

カスタム**ERC20**トークンはサポートされていません。
これは、システムの複雑性を不必要に高めるからです。
ただし、`refundFrom`関数をDEXと組み合わせることで、カスタムトークンに対する返金機能を提供できます。

:::message
カスタム**ERC20**トークンがサポートされない理由は、システムの複雑性を増加させるためです。
具体的には、カスタム**ERC20**トークンは標準的な**ERC20**インターフェースに加えて独自の機能や挙動を持つことが多く、それらをサポートするためには追加のコードを書く必要があります。
例えば、独自の手数料モデルを持つトークンや、特定の条件下でのみトランザクションを許可するトークンなどです。
これらの特殊な挙動を処理するためには、コントラクトに多くの例外処理や追加のセキュリティチェックを実装する必要があり、それが全体としてのシステムの複雑性を高めることになります。

一方で、`refundFrom`関数を分散型取引所（DEX）と組み合わせることで、カスタムトークンに対する返金機能を実現できることがあります。
DEXは多種多様なトークンを扱うため、それらのトークンに特化した処理を行うことができます。
ユーザーがDEXを通じてカスタムトークンを交換する場合、DEXはそのトークンの特殊なルールを処理し、返金処理を実行することができるわけです。
この方法により、返金機能を持つコントラクト自体はシンプルに保ちつつ、カスタムトークンに対応する柔軟性をDEXが提供する形になります。
:::

一括返金機能はオプションです。
将来的にアカウント抽象化が導入されると、こうした一括処理はさらに簡単になるでしょう。
しかし、現時点でも一括返金はガスコストを節約するために適切に実装されるべきです。

## 後方互換性

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

## セキュリティ考慮事項

`refund`関数には、プログラムが予期せぬ方法で複数回呼び出されることによるセキュリティリスクが存在します。
このリスクを防ぐためには、**ETH**を送る操作を行う前にトークンが正しく破棄されているかを確認する必要があります。
これは「**チェック、効果、やり取り（checks, effects, interactions）**」の順番で処理を行うべきです。
まず最初に、セキュリティチェックを行い、次にトークンの破棄のような内部状態を変更する処理を実行し、最後に**ETH**の送金のような外部コントラクトとのやり取りを行います。
この手順に従うことで、再入可能性によるリスクを軽減できます。

## 引用

elie222 (@elie222), Gavin John (@Pandapip1), "ERC-5507: Refundable Tokens," Ethereum Improvement Proposals, no. 5507, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5507.

## 最後に

今回は「**ERC20**、**ERC721**、**ERC1155**トークンに払い戻し機能の追加を提案している規格である**ERC5507**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58


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