[ERC7425] ブロックチェーン上で管理されるトークン化されたリザーブの仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、ステークホルダーが参加できる、オンチェーンでの透明性ある準備基金の仕組みを提案している規格である**ERC7425**についてまとめていきます！

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

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

:::note warn
**7425**は現在（2023年11月1日）では「**Draft**」段階です。
:::

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

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

# 概要

この提案は、トークン化されたリザーブ（資金のプール）メカニズムに関するものです。
このリザーブは、所有者のコントラクト上のアクションを監視できるように設計されています。
**ERC4626**を使用して、ステークホルダーはリザーブ内のアクションをサポートするためのシェアを作成できます。

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

https://qiita.com/cardene/items/99db4ee3074ab6c8c4e2

:::note info
**リザーブ（準備金）**

通常の資金が不足した場合のバックアップとして設けられた、エンティティや組織が保有する資金のことを指しています。
今回の規格におけるリザーブは、トークン化され、特定の基準を満たしたりステークホルダーとの連携を透明化するために使用されることが提案されています。
:::

- **リザーブ（資金のプール）**
    - リザーブは、資金のプールを表します。
    - このプールは、特定の目的のために使用される予定です。
- **オンチェーンアクションの監視**
    - リザーブの所有者は、コントラクト内で実行されたアクションを監視できます。
    - これにより、アクションが透明かつ公正に行われていることが確認されます。
- **ERC4626トークン**
    - ステークホルダーは、リザーブ内の特定のアクションをサポートするために、**ERC4626**トークンを作成できます。
    - これにより、コミュニティ全体の支持を受けることができます。

:::note info
このメカニズムは、ブロックチェーン上での透明性とコミュニティの参加を促進するのに役立ちます。
リザーブの所有者は、コントラクトを介して行われたアクションに関する情報を提供し、コミュニティはそのアクションをサポートするためのシェアを作成できます。
これにより、意図が明確に伝えられ、コミュニティとのやり取りがスムーズに行えます。
:::

# 動機

トークン化されたリザーブ（資金の準備金）は、トークン化された保管庫の一部であり、その目標は通常の資金が不足した場合のバックアップとして、エンティティが実際に持っているリアルワールドのリザーブに似たトークン化したリザーブを作成することです。

:::note info
**エンティティ**
一般的に法的な主体や組織、あるいは特定の実体を指すものとして使用されています。
今回の規格においては、リザーブを管理する主体や、ステークホルダーとのやり取りを行う組織や個人を指しています。
:::

現実世界では、エンティティはリザーブ資金にアクセスする前に特定の基準を満たす必要があります。
しかし、ブロックチェーン上では、エンティティはステークホルダーを自身の基準に組み込むことができます。
これにより、ブロックチェーン上に参加するエンティティは、ステークホルダーとの透明性を持つことができます。

- **トークン化されたリザーブ**
    - エンティティが通常の資金が不足した場合に備えて保有するリザーブ（準備金）をトークンとして表現したものです。
    - リアルワールドのリザーブと同様に、特定の基準を満たす必要があります。
- **基準の設定**
    - 現実世界では、エンティティはリザーブ資金にアクセスするために特定の条件を満たす必要があります。
    - しかし、ブロックチェーン上では、エンティティはステークホルダーを自身の基準に組み込むことができます。
    - つまり、ステークホルダーの意向やサポートを考慮して基準を設定できます。
- **透明性の向上**
    - このメカニズムにより、ブロックチェーン上に存在するエンティティは、ステークホルダーに対してリザーブへのアクセス基準を透明化できます。
    - これにより、エンティティはステークホルダーとのやり取りが円滑に行え、信頼性を高めることができます。

このアプローチは、ブロックチェーンに関わる組織やエンティティにとって、リザーブの管理とステークホルダーとの連携を効果的に行う手段となります。

# 仕様

## 定義

- `owner`（オーナー）
    - リザーブの作成者です。
    - リザーブの所有者で、リザーブを管理および制御します。
- `user`（ユーザー）
    - 特定の提案のステークホルダーです。
    - 特定の提案に関連する利害関係者で、提案に影響を与えることができます。
- `reserve`（リザーブ）
    - トークン化されたリザーブのコントラクトです。
    - これは、リザーブのトークン化が行われる契約で、資金の保管とリザーブの運用を可能にします。
- `proposal`（提案）
    - オーナーがコントラクトからの資金引き出しを希望するときに発生するアクションです。
    - 提案は、リザーブから資金を引き出すための要求であり、ステークホルダーの承認が必要です。

## Constructor

コントラクトのコンストラクタは、コントラクトが初期化される時に一度実行される関数です。

- `name`（名前）
    - **ERC20**トークンの名前です。
    - この名前はトークンの識別に使用されます。
- `ticker`（ティッカー）
    - **ERC20**トークンのティッカーシンボルです。
    - 通常、トークンを簡潔に表す略語です。
- `asset`（アセット）
    - **ERC4626**の基礎となる**ERC20**トークンのアドレスです。
    - このアセットがリザーブのバックアップとして使用されます。
- `rAuth`（rAuth）
    - プライマリな承認済みユーザーです。
    - 主要な認証ユーザーで、リザーブの操作権限を持つことができます。
- `rOwner`（rOwner）
    - リザーブのオーナーです。
    - リザーブのコントラクトを所有し、管理する主体です。

## インターフェース

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

pragma solidity ^0.8.0;
import "./ERC4626.sol";
    
interface TokenReserve is ERC4626{
	/**
	* @dev Event emitted after a new proposal is created
	*/
	event proposals(
	    	address indexed token,
	    	uint256 indexed proposalNum,
	    	uint256 indexed amount,
		address recipient
	);
	/**
	* @dev Event emitted after a new deposit is made by the owner
	*/
	event depositR(
		address indexed token,
	    	uint256 indexed amount,
	    	uint256 indexed time,
	    	uint256 count
	);
	/** 
	* @dev Get time of a deposit made to reserve with depositReserve()
	* @param count Number matching deposit
	* @return block.timestamp format
	*/
	function depositTime(uint256 count) external view returns (uint256);
	/** 
	* @dev Get amount deposited to reserve with depositReserve() 
	* @param count Number of deposit
	* @return uint256 number of any asset that were deposited
	*/
	function ownerDeposit(uint256 count) external view returns(uint256);
	/**
	* @dev Token type deposited to reserve with depositReserve()
	* - MUST be an address of ERC20 token
	* @param count Number of deposit
	*/
	function tokenDeposit(uint256 count) external view returns(address);
	/**
	* @dev Amount deposited for shares of proposal by the user
	* - MUST be an ERC20 address
	* @param user address of user
	* @param proposal number of the proposal the user deposited
	*/
	function userDeposit(address user, uint256 proposal) external view returns(uint256);
	/**
	* @dev Token used for given proposal
	* - MUST be ERC20 address
	* @param proposal number for requested token
	* @return token address
	*/
	function proposalToken(uint256 proposal) external view returns(address);
	/**
	* @dev Amount withdrawn for given opened proposal
	*/
	function proposalWithdrew(uint256 proposal) external view returns(uint256);
	/**
	* @dev Amount received for given closed proposal
	*/
	function proposalDeposited(uint256 proposal) external view returns(uint256);
	/**
	* @dev Make a deposit to a proposal creating new shares with deposit function from ERC-4626
	* - MUST be opened proposal
	* - MUST NOT be opened proposal that was closed
	* NOTE: using the deposit() will cause assets to not be accounted for in a proposal
	* @param assets Amount being deposited
	* @param receiver Address of depositor
	* @param proposal Number associated proposal
	*/
	function proposalDeposit(uint256 assets, address receiver, uint256 proposal) external virtual returns(uint256);
	/**
	* @dev Burn shares, receive 1 to 1 value of shares
	* - MUST have closed proposalNumber
	* - MUST have userDeposit greater than or equal to userWithdrawal
	* @param assets Amount being deposited
	* @param receiver Address of receiver
	* @param owner Address of token owner
	* @param proposal Number associated proposal
	*/
	function proposalWithdraw(uint256 assets, address receiver, address owner, uint256 proposal)external virtual returns(uint256);
	/**
	* @dev Issue new proposal
	* - MUST create new proposal number
	* - MUST account for amount withdrawn
	* - MUST emit proposals event
	* @param token Address of ERC-20 token
	* @param amount Token amount being withdrawn
	* @param receiver Address of token recipient
	*/
	function proposalOpen(address token, uint256 amount, address receiver) external virtual returns (uint256);
	/**
	* @dev Make deposit and/or choose to close an opened proposal
	* - MUST account for amount received
	* - MUST be a proposal that is less than or equal to current proposal
	* - MUST emit proposals event
	* @param token Address of ERC-20 token
	* @param proposal Number of desired proposal
	* @param amount Token amount being deposited to the reserve
	* @param close Choose to close the proposal
	*/
	function proposalClose(address token, uint256 proposal, uint256 amount, bool close) external virtual returns (bool);
	/**
	* @dev Optional accounting for tokens deposited by owner
	* - MUST be reserve owner
	* - MUST emit depositR event
	* NOTE: No shares are issued, funds can not be redeemed. Only withdrawn from proposalOpen
	* @param token Address of ERC-20 token
	* @param sender Address of where tokens from
	* @param amount Token amount being deposited
	*/
	function depositReserve(address token, address sender, uint256 amount) external virtual;
}
```

### `proposals`

```javascript
event proposals(
    address indexed token,
    uint256 indexed proposalNum,
    uint256 indexed amount,
    address recipient
)
```

**概要**
新しい提案が作成された時に発行されるイベント。

**詳細**
このイベントは、新しい提案がリザーブ内で作成されたときにトリガーされます。
提案に関する重要な情報を含むトランザクションが発生した時に、他のユーザーやアプリケーションに通知するのに役立ちます。

**パラメータ**
- `token`
    - 提案に関連付けられたトークンのアドレス。
- `proposalNum`
    - 提案の番号や識別子。
- `amount`
    - 提案に関連する金額や数量。
- `recipient`
    - 提案の受取人のアドレス。

---

### `depositR`

```javascript
event depositR(
    address indexed token,
    uint256 indexed amount,
    uint256 indexed time,
    uint256 count
)
```

**概要**
オーナーによる新しい預金が行われた時に発行されるイベント。

**詳細**
このイベントは、リザーブのオーナーが新しい預金を行ったときにトリガーされます。
リザーブ内の資産に関する情報を提供し、トランザクションの発生を記録します。

**パラメータ**
- `token`
    - 預金に使用されたトークンのアドレス。
- `amount`
    - 預金された金額や数量。
- `time`
    - 預金が行われたタイムスタンプ。
- `count`
    - 預金回数やカウント。

---

### `depositTime`

```javascript
function depositTime(uint256 count) external view returns (uint256);
```

**概要**
指定された預金回数に対応するリザーブへの預金時間を取得する関数。

**詳細**
指定された預金回数に対応するリザーブへの預金が行われた時間（ブロックのタイムスタンプ）を取得します。
預金のタイムスタンプは、預金が行われた時間を追跡するのに役立ちます。

**引数**
- `count`
    - 取得したい預金の回数。

**戻り値**
- `uint256`
    - 指定された預金回数に対応する預金時間。

---

### `ownerDeposit`

```javascript
function ownerDeposit(uint256 count) external view returns(uint256);
```

**概要**
指定された預金回数に対応するリザーブへのオーナーによる預金額を取得する関数。

**詳細**
指定された預金回数に対応するリザーブへのオーナーによる預金額を取得します。
オーナーがリザーブに預け入れた金額を返します。

**引数**
- `count`
    - 取得したい預金の回数。

**戻り値**
- `uint256`
    - 指定された預金回数に対応するオーナーの預金額。

---

### `tokenDeposit`

```javascript
function tokenDeposit(uint256 count) external view returns(address);
```

**概要**
指定された預金回数に対応するリザーブへの預金に使用されたトークンのアドレスを取得する関数。

**詳細**
指定された預金回数に対応するリザーブへの預金に使用されたトークンのアドレスを取得します。
特定の預金に使用されたトークンを特定するのに役立ちます。

**引数**
- `count`
    - 取得したい預金の回数。

**戻り値**
- `address`
    - 指定された預金回数に対応する預金に使用されたトークンのアドレス。

---

### `userDeposit`

```javascript
function userDeposit(address user, uint256 proposal) external view returns(uint256);
```

**概要**
特定のユーザーが特定の提案に対して行った預金額を取得する関数。

**詳細**
特定のユーザーが特定の提案に対して行った預金額を取得します。
ユーザーが特定の提案にどれだけの預金を行ったかを返します。

**引数**
- `user`
    - 預金情報を取得したいユーザーのアドレス。
- `proposal`
    - 預金情報を取得したい提案の番号。

**戻り値**
- `uint256`
    - 指定されたユーザーが指定された提案に対して行った預金額。

---

### `proposalToken`

```javascript
function proposalToken(uint256 proposal) external view returns(address);
```

**概要**
指定された提案に使用されたトークンのアドレスを取得する関数。

**詳細**
指定された提案に使用されたトークンのアドレスを取得します。
特定の提案に使用されたトークンを特定するのに役立ちます。

**引数**
- `proposal`
    - 取得したい提案の番号。

**戻り値**
- `address`
    - 指定された提案に使用されたトークンのアドレス。

---

### `proposalWithdrew`

```javascript
function proposalWithdrew(uint256 proposal) external view returns(uint256);
```

**概要**
指定された提案に対する引き出し金額を取得する関数。

**詳細**
指定された提案に対する引き出し金額を取得します。
特定の提案からすでに引き出された金額を返します。

**引数**
- `proposal`（uint256）: 取得したい提案の番号。

**戻り値**
- `uint256`
    - 指定された提案に対する引き出し金額。

---

### `proposalDeposited` 

```javascript
function proposalDeposited(uint256 proposal) external view returns(uint256);
```

**概要**
指定された閉じられた提案に対する受け取った金額を取得する関数。

**詳細**
指定された閉じられた提案に対して受け取った金額を取得します。
閉じられた提案から受け取った金額を返します。

**引数**
- `proposal`
    - 取得したい提案の番号。

**戻り値**
- `uint256`
    - 指定された提案に対する受け取った金額。

---

### `proposalDeposit`

```javascript
function proposalDeposit(uint256 assets, address receiver, uint256 proposal) external virtual returns(uint256);
```

**概要**
**ERC4626**の`deposit`関数を使用して新しい共有権を作成する提案に預金を行う関数。

**詳細**
提案に対して新しい共有権を作成するために、**ERC4626**の`deposit`関数を使用して預金を行います。
提案は既にオープンされている必要があり、かつ閉じられていない提案である必要があります。
なお、`deposit`関数を使用すると、提案内の資産が計上されなくなることに注意してください。

**引数**
- `assets`
    - 預金する金額。
- `receiver`
    - 預金者のアドレス。
- `proposal`
    - 関連する提案の番号。

**戻り値**
- `uint256`
    - 新しい共有権の数量。

---

### `proposalWithdraw`

```javascript
function proposalWithdraw(uint256 assets, address receiver, address owner, uint256 proposal) external virtual returns(uint256);
```

**概要**
共有権を燃やし、1対1の価値の共有権を受け取る関数。

**詳細**
共有権を燃やし、1対1の価値の共有権を受け取ります。
この操作を行うためには、閉じられた提案番号が存在し、ユーザーの預金がユーザーの引き出し以上である必要があります。

**引数**
- `assets`
    - 預金する金額。
- `receiver`
    - 受信者のアドレス。
- `owner`
    - トークンの所有者のアドレス。
- `proposal`
    - 関連する提案の番号。

**戻り値**
- `uint256`
    - 新しい共有権の数量。

---

### `proposalOpen`

```javascript
function proposalOpen(address token, uint256 amount, address receiver) external virtual returns (uint256);
```

**概要**
新しい提案を作成する関数。

**詳細**
新しい提案を作成します。
新しい提案番号を生成し、引き出し金額を計上する必要があります。
また、`proposals`イベントを発行する必要があります。

**引数**
- `token`
    - **ERC20**トークンのアドレス。
- `amount`
    - 引き出すトークンの数量。
- `receiver`
    - トークンの受信者のアドレス。

**戻り値**
- `uint256`
    - 新しい提案の番号。

---

### `proposalClose`

```javascript
function proposalClose(address token, uint256 proposal, uint256 amount, bool close) external virtual returns (bool);
```

**概要**
預金を行い、オープンされた提案を閉じるか選択する関数。

**詳細**
預金を行い、オープンされた提案を閉じるかどうかを選択します。
提案が終了する場合、トークンはリザーブに計上され、そのトークンは提案から引き出すことはできなくなります。
提案が終了しない場合、トークンはリザーブに計上されません。
また、`proposals`イベントを発行する必要があります。

**引数**
- `token`
    - **ERC20**トークンのアドレス。
    - 提案で預けられるトークンの種類を指定します。
- `proposal`
    - 提案番号。
    - 提案を識別するための番号です。
- `amount`
    - リザーブに預けられるトークンの量。
    - 提案からリザーブに預けるトークンの数量を指定します。
- `close`
    - 提案を終了するかどうかを選択するブール値。
    - `true`の場合、提案は終了され、トークンはリザーブに計上されます。
    - `false`の場合、提案は終了されず、トークンはリザーブに計上されません。

**戻り値**

- 成功した場合は`true`。
- 提案の終了とトークンの計上が成功したことを示します。

:::note info
**計上**
一般的にはある項目や金額を正確に記録し、帳簿やレコードに入力することを指します
ブロックチェーンの文脈では、トランザクションや取引の詳細情報をブロックチェーン上の台帳に正確に記録することを指すことがあります。
計上は、ブロックチェーン上でのトランザクションの処理やデータの更新に関連して、データの変更や取引の記録を意味します。

簡単にまとめると、特定のアクションや変更を正確にブロックチェーン上に記録するプロセスです。
:::

---

### `depositReserve`

```javascript
function depositReserve(address token, address sender, uint256 amount) external virtual;
```

**概要**

リザーブの所有者がオプションで預けたトークンを計上する関数。

**詳細**

リザーブの所有者がオプションで預けたトークンの計上を行います。
計上されたトークンはステーク（共有権）を発行せず、資金は引き出すことはできません。
これらのトークンは、`proposalOpen`関数からのみ引き出すことができます。
リザーブの所有者によって実行される必要があります。


**引数**

- `token`
    - **ERC20**トークンのアドレス。
    - 預けられるトークンの種類を指定します。
- `sender`
    - トークンの送信元のアドレス。
    - トークンの送信元を指定します。
- `amount`
    - 預けられるトークンの量。
    - リザーブに預けられるトークンの数量を指定します。

# 補足

この提案は、リザーブファンドの基本的な実装を提供するもので、具体的に指定されていない条件については個別のケースごとに対処する必要があります。
以下にその要点を説明します。

- 各リザーブは、ステーク（共有権）を表現するために**ERC20**標準を使用します。
    - ステークの作成には**ERC4626**を採用します。
- リザーブトークンは、**ERC4626**内での基礎トークンの表現としても機能し、またボールトに基礎トークンを預け入れることによって共有権が作成されることもあります。
- **ERC4626**は、ユーザーの参加を考慮に入れるためにリザーブに実装されます。
    - これにより、提案に参加するユーザーがどのように表現されるかが明示されます。
- ボールト（保管庫）を使用する場合、実装者はユーザーがボールトに入ることに基づいて参加をどのように扱うかを決定できます。
    - たとえば、同じトークンを複数の提案で使用しないようにユーザーを制約することができ、これにより共有権が公平に作成されます。
- 基礎トークンがオープンな提案のためにボールトに預けられると、それらのトークンは提案が閉じられるまでアクセスできなくなります。
- ただし、リザーブの所有者が預け入れて共有権を作成したトークンを引き出すことができないことは、明示的に強制されていません。
    - 必要に応じて、これらのトークンが適切に管理されるように実装できます。

この提案は、リザーブファンドの基本的な概念と、それがどのように機能するかを説明しています。

# 後方互換性

トークン化されたリザーブが**ERC20**および**ERC4626**と互換性があります。

# セキュリティ考慮事項

議論の必要があります。

# 引用

Jimmy Debe (@jimstir), "ERC-7425: Tokenized Reserve [DRAFT]," Ethereum Improvement Proposals, no. 7425, June 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7425.

# 引用

Martin Lundfall (@Mrchico), "ERC-2612: Permit Extension for EIP-20 Signed Approvals," Ethereum Improvement Proposals, no. 2612, April 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2612.

# 最後に

今回は「ステークホルダーが参加できる、オンチェーンでの透明性ある準備基金の仕組みを提案している規格である**ERC7425**」についてまとめてきました！
いかがだったでしょうか？

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