[ERC6357] EOAアドレスが複数コントラクトを1つのトランザクションで実行できる仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、EOAアドレスが単一のトランザクションで、コントラクトの複数呼び出し機能を実行できる機能を提案している規格である**ERC6357**についてまとめていきます！

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

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

:::note warn
**6357**は現在（2023年10月24日）では「**Review**」段階です。
:::

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

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

# 概要

このEIPは、1つの機能、つまり「`multicall`」を含む標準化されたインターフェースです。
これにより、外部所有者アカウント（EOA）は1つのトランザクションでスマートコントラクトの複数の関数を呼び出すことができます。
もし呼び出しの中でどれか1つでも失敗した場合、すべての呼び出しが取り消されます。

# 動機

現在、複数の**ERC721**NFTを転送するには、転送するNFTの数と同じ回数のトランザクションを提出する必要があります。
これにより、ユーザーは各NFTの転送ごとに`21000`ガス料金を支払わなければならないため、ユーザーの資金が無駄に使われてしまいます。

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

https://chaldene.net/erc721

# 仕様

このEIPを実装するコントラクトは、以下のインターフェイスを実装する必要があります。

```javascript
pragma solidity ^0.8.0;

interface IMulticall {
    /// @notice           Takes an array of abi-encoded call data, delegatecalls itself with each calldata, and returns the abi-encoded result
    /// @dev              Reverts if any delegatecall reverts
    /// @param    data    The abi-encoded data
    /// @returns  results The abi-encoded return values
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results);

    /// @notice           OPTIONAL. Takes an array of abi-encoded call data, delegatecalls itself with each calldata, and returns the abi-encoded result
    /// @dev              Reverts if any delegatecall reverts
    /// @param    data    The abi-encoded data
    /// @param    values  The effective msg.values. These must add up to at most msg.value
    /// @returns  results The abi-encoded return values
    function multicallPayable(bytes[] calldata data, uint256[] values) external payable virtual returns (bytes[] memory results);
}
```

---

### `multicall`

```solidity
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results)
```

**概要**

複数のabiエンコードされた呼び出しデータの配列を受け取り、それぞれのデータを使用して自身をデリゲートコールし、結果をabiエンコードして返す関数。

:::note info
**delegatecall（デリゲートコール）**については以下を参考にしてください。
:::

https://chaldene.net/solidity-delegatecall

**詳細**

提供された呼び出しデータの配列に含まれる各データを順番に処理し、それぞれのデータを使用してコントラクト自体をデリゲートコールします。
各呼び出しの結果はabiエンコードされ、配列で返されます。
もしデリゲートコールの中で一つでも`revert`が発生した場合、この関数もリバートします。

**引数**

- `data`
  - abiエンコードされた呼び出しデータの配列。

**戻り値**

- `results`
  - 各呼び出しの結果をabiエンコードしたデータの配列

---

### `multicallPayable`

```solidity
function multicallPayable(bytes[] calldata data, uint256[] values) external payable virtual returns (bytes[] memory results)
```

**概要**

複数のabiエンコードされた呼び出しデータの配列とそれに対応するEtherの値を受け取り、それぞれのデータを使用して自身をデリゲートコールし、結果をabiエンコードして返す関数。

**詳細**

提供された呼び出しデータの配列に含まれる各データを順番に処理し、それぞれのデータを使用してコントラクト自体をデリゲートコールします。
各呼び出しの結果はabiエンコードされ、配列で返されます。
もしデリゲートコールの中で一つでも`revert`が発生した場合、この関数もリバートします。
また、Etherの値も指定された呼び出しに対応するように提供する必要があります。
オプション機能として提案されています。

**引数**

- `data`
  - abiエンコードされた呼び出しデータの配列。
- `values`
  - 各呼び出しに対応するEtherの値の配列。
  - 合計が`msg.value`以下である必要があります。

**戻り値**

- `results`
  - 各呼び出しの結果をabiエンコードしたデータの配列。

---


# 補足

`multicallPayable`は、`msg.value`が分割されるため、常に実装できるわけではないためオプション機能として実装しています。

# 後方互換性

既存のほとんどのマルチコール機能と互換性があります。

# テスト

以下のJavaScriptコードは、Ethersライブラリを使用して、**ERC20**トークンを「`amt`」単位で「`addressA`」と「`addressB`」の両方にアトミックに転送するものです。

:::note info
**amt**は、**ERC20**トークンを転送する時に、転送する数量を指定するための単位。
:::

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

https://chaldene.net/erc20

```javascript
await token.multicall(await Promise.all([
    token.interface.encodeFunctionData('transfer', [ addressA, amt ]),
    token.interface.encodeFunctionData('transfer', [ addressB, amt ]),
]));
```

# 参考実装

```javascript
pragma solidity ^0.8.0;

/// Derived from OpenZeppelin's implementation
abstract contract Multicall is IMulticall {
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory returndata) = address(this).delegatecall(data);
            require(success);
            results[i] = returndata;
        }
        return results;
    }
}
```

# セキュリティ考慮事項

「`multicallPayable`」は、コントラクトがサポートできる場合にのみ使用すべきです。
単純な実装では、攻撃者が同じEtherを使用して`payable`（支払い可能な）関数を複数回呼び出す可能性があるため、慎重に扱う必要があります。
これにより、不正な攻撃が可能になる可能性があります。
つまり、この機能を提供する際には、セキュリティに十分な注意を払い、不正な利用を防ぐ仕組みを実装することが非常に重要です。

# 引用

Gavin John (@Pandapip1), "ERC-6357: Single-contract Multi-delegatecall [DRAFT]," Ethereum Improvement Proposals, no. 6357, January 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6357.

# 最後に

今回は「EOAアドレスが単一のトランザクションで、コントラクトの複数呼び出し機能を実行できる機能を提案している規格である**ERC6357**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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