# [ERC7604] ERC2612をERC1155形式のNFTに適用してUX良くNFTを送付する仕組みを理解しよう！

> ERC2612やERC4494を使用したPermitのフローは、ERC20やERC721トークンのUXを大幅に改善しました。この規格では、ERC1155にPerimtの仕組みを定期ようすることを提案しています。

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

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC2612**の仕組みを**ERC1155**形式のNFTに適用して、`approve` + `transferFrom`のUXを向上させる仕組みを提案している仕組みを提案している**ERC7604**についてまとめていきます！

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

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

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

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

## 概要

**ERC2612**や**ERC4494**を使用した**Permit**のフローは、**ERC20**や**ERC721**トークンのUXを大幅に改善しました。
この規格では、**ERC1155**に**Perimt**の仕組みを定期ようすることを提案しています。
**ERC1155**では、`tokenId`と`value`の両方を考慮する必要があるため、新しい規格として提案しています。

:::message
**Permit**とは、第3者に自信が保有するトークンの送付をガス代を負担させて実行させることができる仕組みです。
通常、自信が保有しているトークンを運営などの第3者に実行してもらうためには、`approve`関数を実行して送付許可を与える必要があります。
ただ、この処理はトランザクションを発行する必要があるため、少額でもETHが必要になります。
つまりユーザーは少額のETHを入手してからしか実行できないためUXが良いとは言えません。
そこで提案された**ERC2612**や**ERC4494**では、`approve`部分を署名を行うだけにして、コントラクト側で署名の検証が通れば運営などの第3者のアドレスが`approve`と`transferFrom`を実行できるようにする仕組みを提案しています。
これにより、ユーザーはガス代を保有する必要がなくなります。
:::

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

[\[ERC2612\] ERC20のapproveとtransferFromを1つのトランザクションで実行できる仕組みを理解しよう！](/articles/6809173b2ac428b1ccd0)

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

[\[ERC4494\] ERC2612をNFTに適用してUX良くNFTを送付する仕組みを理解しよう！](/articles/f80c6b2f938fa1f54868)

この規格は、**ERC5216**も使用しています。

:::message
**ERC5216**は、**ERC1155**形式のNFTを保有する特定のアドレスが、自信が保有する`tokenId`の保有量の内一部を他のアドレスに`approve`する仕組みを提案しています。
**ERC5216**については以下の記事を参考にしてください。
:::

[\[ERC5216\] 所有しているERC1155トークンの一部を他アドレスへ承認できる仕組みを理解しよう！](/articles/cde40951719798ead6f5)

**ERC20**については以下の記事を参考にしてください。

https://chaldene.net/erc20

**ERC721**については以下の記事を参考にしてください。

https://chaldene.net/erc721

**ERC1155**については以下の記事を参考にしてください。

https://chaldene.net/erc1155

## 動機

**ERC2612**と**ERC4494**では、**ERC20**と**ERC721**にしか**Permit**の仕組みを適用できないため、この規格では**ERC1155**に適用させる仕組みを提案します。

## 仕様

**ERC1155**と**ERC5216**に以下の3つの機能を追加する必要があります。

```js
interface IERC1155Permit {
	function permit(address owner, address operator, uint256 tokenId, uint256 value, uint256 deadline, bytes memory sig) external;
	function nonces(address owner, uint256 tokenId) external view returns (uint256);
	function DOMAIN_SEPARATOR() external view returns (bytes32);
}
```

`permit`実行には、`allowance(owner, spender, tokenId)`で保有している特定の`tokenId`の保有量の一部である`value`を指定し、`nonces(owner, tokenId)`を`1`だけインクリメントし、**ERC5216**で定義されている`Approval`イベントを発行する必要があります。

また、以下の条件も満たす必要があります。

- 現在のブロックタイムが`deadline`以下である。
- トークンの所有者（`owner`）がゼロアドレスでない。
- 指定された`tokenId`の`nonce`（`nonces[owner][tokenId]`）が現在の`nonce`と一致する。
- 署名sigが**secp256k1**や**ERC2098**、**ERC1271**などの有効な署名である。


```js
keccak256(abi.encodePacked(
 hex"1901",
 DOMAIN_SEPARATOR,
 keccak256(abi.encode(
          keccak256("Permit(address owner,address spender,uint256 tokenId,uint256 value,uint256 nonce,uint256 deadline)"),
          owner,
          spender,
          tokenId,
          value,
          nonce,
          deadline))
));
```

これらの条件が満たされない場合、`permit`の実行は失敗します。

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

[\[ERC2098\] Ethereumの署名をよりコンパクトにする仕組みを理解しよう！](/articles/2fb8f63af3b22eb72e33)

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

[\[ERC1271\] コントラクトで署名を検証する仕組みを理解しよう！](/articles/da237b1b8cc185491512)

また、`DOMAIN_SEPARATOR`は**EIP712**に従って定義される必要があります。
`DOMAIN_SEPARATOR`は、他のドメインからのリプレイ攻撃を防ぐために、コントラクトとチェーンで一意でなければなりません。

```js
DOMAIN_SEPARATOR = keccak256(
    abi.encode(
        keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
        keccak256(bytes(name)),
        keccak256(bytes(version)),
        chainid,
        address(this)
));
```

よって、署名のメッセージは以下のような構造になります。

```json
{
  "types": {
    "EIP712Domain": [
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "version",
        "type": "string"
      },
      {
        "name": "chainId",
        "type": "uint256"
      },
      {
        "name": "verifyingContract",
        "type": "address"
      }
    ],
    "Permit": [
	  {
	    "name": "owner".
	    "type": "address"
	  },
      {
        "name": "spender",
        "type": "address"
      },
      {
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "name": "value",
        "type": "uint256"
      },
      {
        "name": "nonce",
        "type": "uint256"
      },
      {
        "name": "deadline",
        "type": "uint256"
      }
    ],
    "primaryType": "Permit",
    "domain": {
      "name": erc1155name,
      "version": version,
      "chainId": chainid,
      "verifyingContract": tokenAddress
  },
  "message": {
    "owner": owner,
    "spender": spender,
    "tokenId": tokenId,
    "value": value,
    "nonce": nonce,
    "deadline": deadline
  }
}}
```

上記に加えて以下の条件があります。

`permit`関数は署名者がゼロアドレスでないことをチェックする必要があります。

この仕組みでは、`msg.sender`（関数実行アドレス）を参照する仕組みがなく、`permit`関数の呼び出しもとは任意のアドレスにすることができます。

### EIP165

この規格には**EIP165**が必要になります。
**EIP165**を使用することで、他のNFTコントラクトがこのEIPを実装しているか簡単に検証できるようになります。
この規格で提案されているコントラクトが**EIP165**で返されるインターフェースIDは`0x7409106d`です。

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

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

## 補足

`permit`関数は追加のトランザクションを必要とせずに、`safeTransferFrom`を実行できるようになります。
このフォーマットは予期しないコードへの呼び出しを避けるように設計されています。
また、リプレイ保護のために`nonces`マッピングが提供されています。

一般的な`permit`の使用ケースとして、リレーヤーが所有者に代わって**Permit**を提出するシナリオがあります。
この場合、リレーヤーは**Permit**を提出するかどうかの選択権を持つことになります。
この点が懸念される場合、所有者は`deadline`を近い将来に設定することで、**Permit**の有効期間を制限できます。
また、`deadline`引数を`uint(-1)`に設定することで有効期限のない**Permit**を作成することもできます。

**ERC712**形式のメッセージは**ERC2612**や**ERC4494**での使用を考慮しており、多くのウォレットプロバイダで広く採用されているためこのEIPでも採用されています。

**ERC2612**では`approve`する`value`に焦点を当てていて、**ERC4494**では`approve`する`tokenId`に焦点を当てています。
このEIPは`permit`によって`approve`されるNFTの`tokenId`と`value`の両方に焦点を当てています。
また、**ERC1155**では`value`による`approve`をネイティブにサポートしていないため、**ERC5216**が必要になります。


**ERC2612**では署名が`v`, `r`, `s`のコンポーネントに分割されていますが、このEIPでは可変長のバイト配列を使用して**EIP2098**署名（`64`バイト）をサポートします。
**EIP2098**署名は`r`, `s`, `v`コンポーネント（`65`バイト）から簡単に分離・再構成することができません。

## セキュリティ

以下と同じ内容です。

[\[ERC4494\] ERC2612をNFTに適用してUX良くNFTを送付する仕組みを理解しよう！](/articles/f80c6b2f938fa1f54868)

## 引用

calvbore (@calvbore), emiliolanzalaco (@emiliolanzalaco), "ERC-7604: ERC-1155 Permit Approvals [DRAFT]," Ethereum Improvement Proposals, no. 7604, January 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7604.

## 最後に

今回は「**ERC2612**の仕組みを**ERC1155**形式のNFTに適用して、`approve` + `transferFrom`のUXを向上させる仕組みを提案している仕組みを提案している**ERC7604**」についてまとめてきました！
いかがだったでしょうか？

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