# [ERC777] トークンコントラクトの標準インターフェースの仕組みを理解しよう！

> このEIPは、トークンのコントラクトに関する標準的なインターフェースを提案しています。これにより、既存のERC20規格との互換性を保ちながら、トークンを取り扱う時により多くの機能と柔軟性を提供することができます。

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

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC20**規格と互換性を持ちながら、トークンコントラクトの標準インターフェースと動作を提案している**ERC777**についてまとめていきます！

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

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

## 概要

このEIPは、トークンのコントラクトに関する標準的なインターフェースを提案しています。
これにより、既存の**ERC20**規格との互換性を保ちながら、トークンを取り扱う時により多くの機能と柔軟性を提供することができます。

具体的には、この提案では以下のポイントが含まれています。

#### トークンの代理送信（オペレーター機能）
この提案では、他のユーザーやコントラクトがあなたの代わりにトークンを送信するための機能を定義しています。
これにより、例えばユーザーの追加の許可を必要とせずに友達があなたのトークンを送信できるようになります。

#### トークンの制御強化（送受信フック）
トークン保有者は、トークンが送信されたり受信されたりする時に、特定のアクションを実行するための「**フック**」を設定できるようになります。
これにより、トークンを受け取った後、自動的に何かアクションを起こすことが可能です。

#### ERC-1820の活用
この提案は、**ERC1820**と呼ばれる仕組みを利用しています。
これにより、トークンがどのコントラクトやアドレスに送信されたかを通知することが容易になります。
また、これによって過去にデプロイされたコントラクトともスムーズに連携できます。

:::message
まとめると、このEIPはトークンを取り扱う際に、他の人が代理で送信したり、トークンの送受信に関するカスタムなアクションを設定したりすることができる新しい方法を提案しています。
同時に、過去の規格との互換性も考慮されています。
:::

## 動機

この新しい標準は、既に使われている**ERC20**トークン規格を改善することを目指しています。
改善による主な利点は以下の通りです。

1. **送金の統一哲学**
提案されている新しい標準は、Ether（イーサ）の送金哲学と同じ考え方を持っています。
つまり、トークンは`send(dest, value, data)`という方法で送ることができます。

2. **トークン送金と受信のコントロール**
コントラクトと通常のアドレスの両方が、`tokensToSend`フックを登録することで、送るトークンを制御し、必要に応じて送金を拒否することができます（拒否はフック関数内でエラーを返すことで行われます）。

3. **トークン受信のコントロール**
コントラクトと通常のアドレスの両方が、`tokensReceived`フックを登録することで、受け取るトークンを制御し、必要に応じて受信を拒否することができます（拒否はフック関数内でエラーを返すことで行われます）。

4. **一括送金と通知**
`tokensReceived`フックを使うことで、トークンをコントラクトに送金し、同時にコントラクトに通知することができます。
これによって、**ERC20**のような二度の呼び出し（`approve`/`transferFrom`）を必要とせずに済みます。

5. **オペレーターの承認と取り消し**
トークン保有者は、代理でトークンを送金できるオペレーターを「**承認**」または「**取り消し**」できます。
これらのオペレーターは、取引所や小切手処理システム、自動課金システムなど、特定のコントラクトを指します。

:::message
**オペレーター**
取引所や小切手処理システム、自動課金システムなどの特定のコントラクトを指しています。

具体的に説明すると、トークン保有者は、自分の代わりにトークンを送信できる特定のコントラクトを「**オペレーター**」として承認することができます。
これは、取引所で取引を行う場合や、自動的な支払い処理が必要な場合に役立ちます。
例えば、以下のような状況を考えてられます。

1. **取引所**
トークン保有者が取引所でトークンを売買する場合、取引所はそのトークンを保有者の代わりに送信することがあります。
このとき、取引所は「**オペレーター**」として認められており、保有者の許可なしにトークンを移動できます。

2. **小切手処理システム**
あるコントラクトが小切手のような支払いを処理するシステムを運営しているとします。
トークン保有者は、このシステムに対しても同様に「**オペレーター**」として認可を与えることができ、自動的に支払いが行われる仕組みを作ることができます。

3. **自動課金システム**
月額のサービス料や使用料を自動的に支払うシステムも同様です。
トークン保有者は、特定のコントラクトに対して「**オペレーター**」としての権限を与えることで、毎月自動的にトークンが送信され、料金が支払われるようになります。

これらの「**オペレーター**」は、トークン保有者が特定のコントラクトに対して送信の権限を与えることで、自動的なトークンの送受信や支払い処理を行うための仕組みを指しています。
:::

6. **トランザクションデータとオペレーターデータの利用**
トークンの取引には、保有者とオペレーター間で情報を伝えるための`data`および`operatorData`というデータ領域が含まれています。

:::message
トークンの取引には、トークン保有者とオペレーター（特定のコントラクトなど）の間で情報をやり取りするための仕組みがあります。
これは、`data`および`operatorData`という特別なデータ領域を使って実現されます。

具体的な例を挙げて説明します。

1. **data**
トークン保有者とオペレーターは、トークンの取引に関する情報を`data`という領域に含めることができます。
これにより、トークンの送信や受信に関連する追加の情報を伝えることができます。
例えば、取引の目的や詳細な説明などが含まれます。

2. **operatorData**
`operatorData`は、オペレーターがトークンの取引を行う際に、オペレーター自身からトークン保有者に対して情報を伝えるための領域です。
これにより、オペレーターがなぜトークンを送信する必要があるのか、その理由や詳細な情報を伝えることができます。

まとめると、`data`および`operatorData`は、トークンの取引に関する補足的な情報を伝えるための領域です。
トークンの送信や受信だけでなく、取引の目的や理由、詳細な情報などを含めることができるため、より透明性と柔軟性を提供する役割を果たしています。
:::

7. **古いウォレットの互換性**
`tokensReceived`フック関数を持たない古いウォレットとも互換性を持つよう、プロキシコントラクトを使って`tokensReceived`フックを実装することができます。

:::message
古いウォレットには、新しいトークン標準である`tokensReceived`フック関数が実装されていない可能性があります。
しかし、この新しい標準を採用したトークンを古いウォレットでも使用できるようにする方法があります。
それが、プロキシコントラクトを使って`tokensReceived`フックを実装することです。

プロキシコントラクトは、既存のウォレットと処理を実行するコントラクトとの中継処理を行います。
プロキシコントラクトは新しい標準のトークンとやり取りする際に、`tokensReceived`フックを備えています。
これにより、古いウォレットが新しい標準のトークンを正しく受け取り、適切に処理できるようになります。

具体的な説明をすると、次のような流れになります：

1. **プロキシコントラクトの作成**
新しい標準のトークンを使う際には、まずプロキシコントラクトを作成します。
このプロキシコントラクトは、`tokensReceived`フックを実装しているため、新しいトークンとのやり取りを正しく処理できます。

2. **古いウォレットとのやり取り**
古いウォレットは、新しい標準のトークンとの代わりに、プロキシコントラクトとやり取りします。
この際、プロキシコントラクトが`tokensReceived`フックを持っているため、トークンの送信や受信が適切に処理されます。

3. **プロキシコントラクトの役割**
プロキシコントラクトは、古いウォレットと新しいトークンとの橋渡しを行う役割を果たします。
古いウォレットは、実際にはプロキシコントラクトを操作しているため、新しい標準に適合するような振る舞いが実現されます。

プロキシコントラクトは古いウォレットと新しいトークンとの間でコミュニケーションを円滑にするための仕組みです。
古いウォレットが新しい標準のトークンを適切に取り扱えるようにし、互換性を確保する役割を果たします。
:::

## 仕様

### ERC777Tokenコントラクト

```javascript
interface ERC777Token {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function totalSupply() external view returns (uint256);
    function balanceOf(address holder) external view returns (uint256);
    function granularity() external view returns (uint256);

    function defaultOperators() external view returns (address[] memory);
    function isOperatorFor(
        address operator,
        address holder
    ) external view returns (bool);
    function authorizeOperator(address operator) external;
    function revokeOperator(address operator) external;

    function send(address to, uint256 amount, bytes calldata data) external;
    function operatorSend(
        address from,
        address to,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    function burn(uint256 amount, bytes calldata data) external;
    function operatorBurn(
        address from,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );
    event Minted(
        address indexed operator,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );
    event Burned(
        address indexed operator,
        address indexed from,
        uint256 amount,
        bytes data,
        bytes operatorData
    );
    event AuthorizedOperator(
        address indexed operator,
        address indexed holder
    );
    event RevokedOperator(address indexed operator, address indexed holder);
}
```

1. **インターフェースの実装**
トークンコントラクトは、上記の**ERC777Token**インターフェースを実装する必要があります。
この実装は、以下に記載されている仕様に従う必要があります。

2. **ERC-1820への登録**
トークンコントラクトは、自身のアドレスを**ERC1820**を介して**ERC777Token**インターフェースに登録しなければなりません。
これは、**ERC1820**レジストリの`setInterfaceImplementer`関数を呼び出すことによって行います。
その際、アドレスとしてトークンコントラクトのアドレスを、`implementer`としても同じくトークンコントラクトのアドレスを使用し、インターフェースハッシュとして**ERC777Token**の`keccak256`ハッシュ（`0xac7fbab5f54a3ca8194167523c6753bfeb96a445279294b6125b68cce2177054`）を使用します。

:::message
トークンコントラクトは、自身のアドレスを`ERC777Token`インターフェースに登録する必要があります。
これは、`ERC1820`レジストリの`setInterfaceImplementer`関数を呼び出すことで実現されます。
具体的な手順を説明します。

1. **トークンコントラクトのアドレスを登録**
トークンコントラクトは、自身のアドレスを`ERC777Token`インターフェースに登録するために、`ERC1820`レジストリの`setInterfaceImplementer`関数を呼び出します。

2. **アドレスとimplementerの指定**
`setInterfaceImplementer`関数を呼び出す際、以下の情報を指定します。
    - `address`
        - トークンコントラクトのアドレス。
    - `implementer`
        - 同じくトークンコントラクトのアドレス。
    - `interface hash`
        - `ERC777Token`の`keccak256`ハッシュ。（`0xac7fbab5f54a3ca8194167523c6753bfeb96a445279294b6125b68cce2177054`）

これにより、`ERC1820`レジストリはトークンコントラクトのアドレスに対して、`ERC777Token`インターフェースを実装しているという情報を持つことになります。
これによって、他のコントラクトやアドレスがトークンコントラクトとのやり取りを行う際、`ERC1820`を通じてインターフェースの情報を確認することができます。

まとめると、トークンコントラクトは`ERC1820`を使用して自身のアドレスを`ERC777Token`インターフェースに登録し、他のコントラクトやアドレスがトークンコントラクトとやり取りする際に、正しいインターフェース情報を提供する仕組みを確立します。
:::

3. **ERC777関数の切り替え**
コントラクトが`ERC777`関数を有効または無効にするスイッチがある場合、そのスイッチがトリガーされるたびに、トークンは**ERC777Token**インターフェースをトークンコントラクトのアドレスに対して登録または登録解除する必要があります。
登録解除は、`setInterfaceImplementer`を呼び出すことで行います。
具体的には、アドレスとしてトークンコントラクトのアドレス、インターフェースハッシュとして**ERC777Token**の`keccak256`ハッシュ、`implementer`として`0x0`を使用します。

4. **取引時の金額と残高**
トークンコントラクトとのやり取りでは、すべての金額と残高は符号なし整数（**unsigned integers**）として扱われます。
内部的には、すべての値がトークンの`1E-18`の単位で格納されます。
表示単位（エンドユーザーに表示するための単位）は、内部の単位の`1018`分です。

5. **内部の単位と表示単位**
内部の単位は、`wei`に似ており、表示単位は`ether`に似ています。
これは、**ERC20**の`decimals`関数が`18`を返すのと同等です。
例えば、ユーザーの残高が`500,000,000,000,000,000（0.5×1018）`である場合、ユーザーインターフェースはユーザーに`0.5`トークンを表示しなければなりません。
ユーザーが`0.3`トークンを送信したい場合、コントラクトは`300,000,000,000,000,000（0.3×1018）`という金額で呼び出される必要があります。

:::message
1. **符号なし整数（unsigned integers）の使用**
トークンコントラクトとのやり取りでは、すべての金額や残高は符号なし整数として扱われます。
符号なし整数は負の値を表現せず、非負の整数のみを取り扱うデータ型です。
これにより、取引や計算において正確な数値処理が行えます。

2. **内部の単位（1E-18）の使用**
トークンの内部的な表現では、すべての値がトークンの`1E-18`の単位で格納されます。
これは、非常に小さな値で、トークンの最小単位を示しています。
この単位により、高い精度で小数点以下の取引や計算が可能になります。

3. **表示単位の理解**
エンドユーザーに対してトークンの残高や金額を表示する際には、内部の単位をエンドユーザーが理解しやすいように変換します。
表示単位は、内部の単位の`1018`分です。
これにより、エンドユーザーにとって分かりやすい形でトークンの量を表示することができます。

具体例を考えてみましょう。

- 内部の単位（1E-18）でトークン残高が`1000000000000000000`（1トークン）の場合、エンドユーザーには表示単位で`1`トークンとして表示されます。
- エンドユーザーが`0.5`トークンを送信したい場合、トークンコントラクトに対しては内部の単位で`500000000000000000`を指定する必要があります。

まとめると、トークンのやり取りでは正確な数値処理と高い精度が保たれ、エンドユーザーには分かりやすい表示が提供されるために、内部の単位（`1E-18`）と表示単位（`1018`分）が使われています。
:::

6. **ABIから生成されるユーザーインターフェース**
トークンコントラクトのABIから生成されるユーザーインターフェースは、内部の単位を使用して表示される場合があります。
ただし、これが明確に示される必要があります。
例えば、`uint256`型として表示することで、内部の単位が使用されていることがわかるようにします。

:::message
まとめると、トークンコントラクトは特定の仕様に従って**ERC777Token**インターフェースを実装し、**ERC1820**を通じて自身を登録します。
また、金額や残高は内部の単位と表示単位という2つの単位を使用して取り扱われ、プロキシコントラクトを使って古いウォレットでも正しく動作するような仕組みがあります。
:::

### 関数

#### name

識別子: `06fdde03`

```javascript
function name() external view returns (string memory)
```

トークンの名前を取得する関数。
例えば、「**MyToken**」といった名前が返されます。

#### symbol

識別子: `95d89b41`

```javascript
function symbol() external view returns (string memory)
```

トークンのシンボルを取得する関数。
例えば、「**MYT**」といったシンボルが返されます。

#### totalSupply

識別子: `18160ddd`

```javascript
function totalSupply() external view returns (uint256)
```

発行されたトークンの総数を取得する関数。
`balanceOf`関数によって返されたすべてのアドレスの残高の合計と等しくなる必要があります。
また、すべての`Minted`イベントで定義されたすべての発行済みトークンの合計から、すべての`Burned`イベントで定義されたすべてのバーン済みトークンの合計を引いたものに等しくなる必要があります。

#### balanceOf

識別子: `70a08231`

```javascript
function balanceOf(address holder) external view returns (uint256)
```

特定のアドレスのトークン残高を取得する関数。
`0`以上の値を返す必要があります。

**引数**
- `holder`
    - トークン残高を取得したいアドレス。

#### granularity

識別子: `556f0dc7`

```javascript
function granularity() external view returns (uint256)
```

トークンの分割できない最小単位を取得する関数。
分割単位は内部の単位で、いつでもトークンを発行したり送信したり焼却したりする際に使用される、最も小さなトークンの単位です。

以下のルールが分割単位に関して適用されます。

1. 分割単位の値は、トークンの作成時に設定されなます。
2. 分割単位の値は一度設定されたら変更できません。
3. 分割単位の値は1以上です。
4. すべての残高は分割単位の倍数です。
5. 任意のトークンの発行、送信、焼却操作は分割単位の倍数で実行されます。
分割単位の倍数でない操作は無効であり、トランザクションはリバートされます。

**⚠️ 注意**
ほとんどのトークンは完全に分割可能であるべきです。
トークンをいかなる分数も許可しない理由がない限り、この関数は通常`1`を返すべきです。

:::message
まとめると、`granularity`関数はトークンの最小単位を示し、トークンの発行や送信、焼却の際に使用される単位を定義します。
この単位は、トークンの性質を確定させる重要な要素であり、ルールに従って運用される必要があります。
また、**ERC20**互換性においても`decimals`関数が重要であり、この関数によって小数点以下の桁数が定義されます。
:::

### オペレーター

**オペレーター**は、特定の保有者の代わりにトークンを送信したり焼却したりすることが許可されているアドレスです。

#### オペレーターの許可とイベント
- アドレスが特定の保有者のオペレーターになると、`AuthorizedOperator`イベントが発行されます。
    - `AuthorizedOperator`イベントでは、オペレーター（トピック 1）と保有者（トピック 2）はそれぞれオペレーターと保有者のアドレスになります。
- 保有者がオペレーターを取り消す場合、`RevokedOperator`イベントが発行されます。
    - `RevokedOperator`イベントでも、オペレーター（トピック 1）と保有者（トピック 2）はそれぞれオペレーターと保有者のアドレスになります。
- 注意
    - 保有者は同時に複数のオペレーターを持つことができます。

#### デフォルトオペレーター
- トークンはデフォルトオペレーターを定義することができます。
- デフォルトオペレーターは、すべての保有者の代わりに自動的にトークンを送信または焼却できるオペレーターです。
- デフォルトオペレーターを定義する際には、`AuthorizedOperator`イベントは発行されません。
- 以下のルールがデフォルトオペレーターに適用されます。
    - トークンコントラクトは作成時にデフォルトオペレーターを定義します。
    - デフォルトオペレーターは不変でなければなりません。
        - つまり、トークンコントラクトはデフォルトオペレーターを追加または削除してはいけません。

#### 一般的なオペレーターのルール
- アドレスは常に自身のオペレーターである必要があります。
    - したがって、アドレスは自身のオペレーターとして取り消すことはありません。
- アドレスが特定の保有者のオペレーターである場合、`isOperatorFor`関数は`true`を返します。
- アドレスが特定の保有者のオペレーターでない場合、、`isOperatorFor`関数は`false`を返します。

#### イベントの発生
- 保有者がアドレスをオペレーターとして許可すると、`AuthorizedOperator`イベントが発行されます。
- 保有者がアドレスをオペレーターから取り消すと、`RevokedOperator`イベントが発行されます。

#### ⚠️ 注意
- 保有者は既に許可されたオペレーターを再度許可できます。
    - これは毎回`AuthorizedOperator`イベントが発行されます。
- 保有者は既に取り消されたオペレーターを再度取り消すことができます。
    - これは毎回`RevokedOperator`イベントが発行されます。

#### AuthorizedOperator

```javascript
event AuthorizedOperator(address indexed operator, address indexed holder)
```

オペレータに設定された時に発行されるイベント。

**パラメータ**

- `operator`
    - オペレーターアドレス。
- `holder`
    - トークン保有者のアドレス。
    - オペレーターを許可したアドレス。

#### RevokedOperator

```javascript
event RevokedOperator(address indexed operator, address indexed holder)
```

オペレータが取り消された時に発行されるイベント。

**パラメータ**

- `operator`
    - オペレーターアドレス。
- `holder`
    - トークン保有者のアドレス。
    - オペレーターの許可を取り消したアドレス。

オペレータを管理するために、後述の`defaultOperators`、`authorizeOperator`、`revokeOperator`、 `isOperatorFor`関数を実装する必要があります。
トークンコントラクトは、オペレータを管理するために他の関数を実装できます。

#### defaultOperators

識別子: `06e48538`

```javascript
function defaultOperators() external view returns (address[] memory)
```

デフォルトのオペレーターの配列を取得する関数。
デフォルトのオペレーターが存在しない時、空の配列を返します。

#### authorizeOperator

識別子: `959b8c3f`

```javascript
function authorizeOperator(address operator) external
```

`msg.sender`のアドレスのオペレータを設定する関数。
トークン保有者（`msg.sender`）は常に自身のオペレーターであり、この権利は取り消すことはありません。
したがって、この関数が保有者（`msg.sender`）を自身のオペレーターとして設定しようとした場合（つまり、オペレーターが`msg.sender`と等しい場合）、この関数はリバート（取引を元に戻す）します。

**引数**

- `operator`
    - オペレーターに設定するアドレス。

#### revokeOperator

識別子: fad8b32a

```javascript
function revokeOperator(address operator) external
```

`msg.sender`のアドレスのオペレータ権限を取り除く関数。
トークン保有者（`msg.sender`）は常に自身のオペレーターであり、この権利は取り消すことはありません。
したがって、この関数が保有者（`msg.sender`）を自身のオペレーターから取り除こうとした場合（つまり、オペレーターが`msg.sender`と等しい場合）、この関数はリバート（取引を元に戻す）します。

**引数**

- `operator`
    - オペレーターから取り消すアドレス。

#### isOperatorFor

識別子: `d95b6371`

```javascript
function isOperatorFor(
    address operator,
    address holder
) external view returns (bool)
```

特定のアドレスがトークン保有者アドレスのオペレータか確認する関数。

**引数**

- `operator`
    - オペレーターか確認したいアドレス。
- `holder`
    - トークンの保有者アドレス。

**戻り値**
トークン保有者アドレスのオペレーターかどうかの`bool`値。

**⚠️ 注意**
どのアドレスが指定された所持者のオペレータであるかを知るには、各デフォルトオペレータの所持者アドレスを仕様して`isOperatorFor`を呼び出し、その所持者の`AuthorizedOperator`、`RevokedOperator`イベントを解析しなければならない。

### トークンの送付

オペレーターが保有者から受取アドレスへトークンを特定の量と関連データ、オペレーターデータと共に送信する場合、トークンコントラクトは以下のルールを適用します。

1. 任意の許可されたオペレーターは、トークンを受取アドレスに送れます（ただし`0x0`には送れません）。
2. トークンの保有者の残高は送信するトークンの量だけ減少します。
3. 受取アドレスの残高は送信されるトークンの量だけ増加します。
4. トークンの保有者の残高は、送信後も`0`以上の値になるように調整されます。
送信前のトークン保有者の残高は送信予定のトークン量よりも多いか確認します。
5. トークンコントラクトは、`Sent`イベントを発行します。
6. オペレーターは、オペレーターデータに情報を含めることができます。
7. トークンの保有者が**ERC1820**を介して**ERC777TokensSender**の実装を登録している場合、トークンコントラクトは保有者の`tokensToSend`フックを呼び出す必要があります。

:::message
**tokensToSend**

このフックは、保有者がトークンを送信する際にトークンコントラクトによって呼び出される「**カスタムコードの断片**」です。
保有者はこのフックを使用して、トークンの送信前に追加のロジックや処理を行うことができます。
例えば、トークンの送信に特別な条件を設定したり、追加のデータをトランザクションに含めたりすることができます。

保有者が`tokensToSend`フックを登録している場合、トークンコントラクトはトークンの送信操作を実行する前に、保有者のこのフックを呼び出します。
保有者はその中で必要な処理やチェックを行い、トークンの送信を続行するかどうかを決定できます。
トークンコントラクトはこのフックの結果に基づいて、トークンの送信操作の実行を決定します。

この仕組みにより、保有者はトークンの送信に関する追加のロジックをカスタマイズできるため、より柔軟なトークン取引が可能となります。
:::

8. 受取アドレスが**ERC1820**を介して**ERC777TokensRecipient**の実装を登録している場合、トークンコントラクトは受取アドレスの`tokensReceived`フックを呼び出す必要があります。
9. 送信の時に使用されるデータとオペレーターデータは、送信プロセス全体で変更されないことが必要です。
そのため、同じデータとオペレーターデータが両方のフックの呼び出しおよび`Sent`イベントの発行に使用されます。

:::message
送信プロセスにおいて、トークンの送信が行われる際には、特定のデータとオペレーターデータが関与します。
これらのデータは、トークンの送信の際にトークンコントラクトや関連するコードによって使用されます。
重要なのは、このデータが送信プロセス全体で変更されず、一貫性を保つことです。

例えば、保有者がトークンを送信する場合を考えてみましょう。
保有者は特定の受取アドレスにトークンを送信し、その際に関連するデータやオペレーターデータを指定します。
このデータは、トークンの送信において必要な情報や処理内容を表しています。

ここで大切なのは、このデータが送信プロセスの中で変更されないことです。
なぜなら、保有者が用意したデータが送信プロセスの途中で変わってしまうと、予期せぬ結果が生じる可能性があるからです。
例えば、トークンの送信操作の途中でデータが変更されると、送信の内容が一致しないために取引が予期せぬ動作を起こす可能性があります。

したがって、データとオペレーターデータは送信プロセス全体で変更されないようにする必要があります。
これを実現するために、同じデータとオペレーターデータが保有者の`tokensToSend`フックの呼び出し、受取人の`tokensReceived`フックの呼び出し、そして`Sent`イベントの発行に使用されます。
これによって、送信プロセス全体で一貫性が保たれ、信頼性のあるトークン取引が確保されるのです。
:::

これにより、オペレーターによるトークンの送信プロセスが明確に定義され、信頼性のあるトークン取引が確保されます。

トークンコントラクトは、以下のいずれかの場合にトークンの送信処理を失敗させます。

- オペレーターアドレスが保有者の許可されたオペレーターでない。
- トークン保有者の残高または受け取りアドレスの残高が、トークンコントラクトで定義されたトークンの最小単位の倍数ではない。
- 受け取りアドレスがコントラクトであり、**ERC1820**を介して**ERC777TokensRecipient**インターフェースを実装していない。
- トークン保有者または受け取りアドレスのアドレスが`0x0`の場合。
- いずれかの残高が負の値（`0`未満）になる。
- 保有者の`tokensToSend`フックが失敗した場合。
- 受け取りアドレスの`tokensReceived`フックが失敗した場合。

トークンコントラクトは、多くの保有者から多くの受け取りアドレスにトークンを送信したり、その逆を行う場合があります。
この時に以下の条件を満たす必要があります。

- 前述の送信ルールは、すべての保有者と受け取りアドレスに適用されます。
- 増加した残高の合計は、送信されたトークン量と等しくなければなりません。
- 減少した残高の合計は、送信されたトークン量と等しくなければなりません。
- 各保有者と受け取りアドレスのペアごとに、対応する金額で`Sent`イベントが発行されます。
- `Sent`イベントからの金額の合計は、送信されたトークン量と等しくなければなりません。

**⚠️ 注意**
- 送金に手数料を適用するような仕組みは、実質的に複数の受取りアドレスへの送信と見なされます。
    - つまり、トークンの送信先と手数料の受取りアドレスが含まれる場合、これは複数のアドレスへの送信とみなされます。
- トークンの移動は、一連のアクションとして連鎖させることができます。
    - 例えば、あるコントラクトがトークンを受け取ると、それを別のアドレスにさらに送信する場合です。
    - この場合、前述の送信ルールは各送信ごとに適用されます。
    - つまり、各送信ごとに残高の増減やイベントの発行が行われ、それぞれの送信がルールに従って処理されます。
- トークンの量をゼロ（`0`）として送信することは許可されており、通常の送信と同様に扱われます。
    - この場合、トークンは実際には送信されませんが、トランザクションが正常に処理されるために必要な操作として認識されます。

トークンの実装要件について、以下のポイントを分かりやすく説明します。

1. **tokensToSendフックとtokensReceivedフックの呼び出し順序**
トークンを送信する際、トークンコントラクトはまず`tokensToSend`フックを呼び出します。
このフックは、トランザクションが送信される前に実行されます。
その後、トークンの送信に応じて保有者の残高が減少し、受け取りアドレスの残高が増加します。
そして、`tokensReceived`フックが呼び出されます。
このフックはトランザクションが完了した後に実行されます。

2. **データフィールドとoperatorDataフィールド**
トークンの送信時には、データフィールドに保有者から提供される情報が含まれます。
これは、通常の**ETH**送金トランザクションのデータフィールドと同様です。
`tokensToSend()`フックと`tokensReceived()`フックのどちらか、または両方は、この情報を使用してトランザクションを受け入れるか拒否するかを判断するために使用できます。
また、`operatorData`フィールドも似たような役割を果たしますが、これはオペレーターによって提供されるものです。
オペレーターはこのフィールドを使用して特定の情報を伝えることができます。
ただし、受け取りアドレスは通常、`operatorData`を無視するか、実行してもログに記録するだけです。

これにより、トークンの送信プロセスが順番に実行されることや、データの提供と処理、そしてオペレーターが情報を伝える仕組みが明確に定義され、信頼性のあるトークンの取引が実現されます。

#### Sentイベント

```javascript
event Sent(
    address indexed operator,
    address indexed from,
    address indexed to,
    uint256 amount,
    bytes data,
    bytes operatorData
)
```

オペレーターアドレスによって、送信元アドレスから送信先アドレスへトークンの量を送信するイベント。

**パラメーター**
- `operator`
    - オペレーターアドレス。
- `from`
    - トークンの保有者アドレス。
- `to`
    - トークンの受け取りアドレス。
- `amount`
    - 送信されたトークンの数量。
- `data`
    - 保有者から提供された情報。
- `operatorData`
    - オペレーターによって提供された情報。

以下に説明する`send`関数と`operatorSend`関数は、トークンの送信を実行するために実装する必要があります。
トークンコントラクトは、他のトークンの送信を行うための関数を実装することもできます。

#### send

識別子: `9bd9bbc6`

```javascript
function send(address to, uint256 amount, bytes calldata data) external
```

トークンの量を`msg.sender`のアドレスからアドレス`to`へ送る関数。

オペレーターと保有者は、両方とも`msg.sender`と同じアドレスである必要があります。

**パラメーター**
- `to`
    - トークンの受け取りアドレス。
- `amount`
    - 送信するトークンの数量。
- `data`
    - 保有者から提供された情報。

#### operatorSend

識別子: `62ad1b83`

```javascript
function operatorSend(
    address from,
    address to,
    uint256 amount,
    bytes calldata data,
    bytes calldata operatorData
) external
```

アドレス`from`からアドレス`to`へ、指定されたトークン量を代理で送信する関数。

**⚠️ 注意**
オペレーターアドレスがアドレス`from`の許可されたオペレーターでない場合、送信プロセスは取り消されます。

`from`と`msg.sender`は同じアドレスである場合があります。
つまり、アドレスは自身のために`operatorSend`を呼び出すことができます。
この呼び出しは、トークンの送信に加えて、オペレーターが`operatorData`の明示的な値を指定することができる点を除いて、送信と同等である必要があります（送信関数ではこれは実行できません）。

パラメーター:
- `from`
    - トークン保有者のアドレス。
- `to`
    - トークンの受け取りアドレス。
- `amount`
    - 送信するトークンの数量。
- `data`
    - 保有者から提供された情報。
- `operatorData`
    - オペレーターから提供された情報。

### トークンのミント
トークンの発行（Minting）とは、新しいトークンを生成するプロセスのことです。
**ERC777**では、トークンの発行を行うための特定の関数を定義していません。
これは、各トークンごとに発行プロセスが異なるため、標準を柔軟に利用できるようにするためです。

ただし、受取り先にトークンを発行する際には次のルールを守る必要があります。

- 任意の受取り先アドレス（ただし`0x0`ではない）に対してトークンを発行できます。
- 総供給量は発行されるトークンの数量だけ増加します。
- アドレス`0x0`の残高は減少させてはいけません。
- 受け取りアドレスの残高は発行されるトークンの数量だけ増加しなければなりません。
- トークンコントラクトは、正しい値で`Minted`イベントを発行する必要があります（`Minted`イベントの定義に従う）。
- 受け取りアドレスが**ERC1820**を介して**ERC777TokensRecipient**の実装を登録している場合、トークンコントラクトは受取り先の`tokensReceived`フックを呼び出す必要があります。
- データとオペレーターデータは、トークンの発行プロセス全体で変更されない必要があります。
    - そのため、`tokensReceived`フックを呼び出す際と`Minted`イベントを発行する際には同じデータとオペレーターデータを使用します。

トークンの発行時に、次のいずれかの場合にトークンコントラクトはトランザクションを中止します。

- 発行後の受取り先の残高が、トークンコントラクトで定義された最小単位の倍数でない場合。
- 受け取りアドレスがコントラクトであり、**ERC1820**を介して**ERC777TokensRecipient**インターフェースを実装していない場合。
- 受取り先のアドレスが`0x0`の場合。
- 受取り先の`tokensReceived`フックが失敗する場合。

トークンコントラクトの作成時における初期トークン供給量は、初期供給を受けるアドレス（複数の場合もあります）に対してトークンが発行されます。
したがって、少なくとも1つ以上の`Minted`イベントが発行され、受取り側の`tokensReceived`フックが呼び出される必要があります。

**ERC20**互換性の要件は以下です。

1. トークンの発行時に`Sent`イベントを発行してはいけません。
ただし、トークンコントラクトが**ERC20**と互換性を持つ場合、`Transfer`イベントを発行します。
この`Transfer`イベントの`from`パラメータは`0x0`に設定されます。
この動作は**ERC20**標準で定義されています。

2. トークンコントラクトは一度に複数の受け取りアドレスに対してトークンを発行することができます。
この場合、次のルールが適用されます。
    - 前述の発行ルールはすべての受け取りアドレスに適用されます。
    - すべての残高の増加の合計は、合計発行量と一致しなければなりません。
    - 各受け取りアドレスごとに対応する数量の`Minted`イベントが発行されます。
    - `Minted`イベントからのすべての数量の合計は、合計発行量と一致しなければなりません。

3. トークンの発行量がゼロ（`0`）の場合でも通常の発行と同じように扱われます。

4. 送信や焼却の際にはデータが保有者から提供されますが、トークンの発行の場合には適用されません。
この場合、データはトークンコントラクトまたはオペレーターによって提供されることがあります。
これにより、特定のデータを期待する保有者に正常な発行を保証できます。

5. `operatorData`フィールドには、オペレーターから提供される情報が含まれます。
通常の**ETH**送信トランザクションのデータフィールドと類似しています。
`tokensReceived()`フックはこの情報を使用してトランザクションを拒否するかどうかを決定できます。

#### Mintedイベント

```javascript
event Minted(
    address indexed operator,
    address indexed to,
    uint256 amount,
    bytes data,
    bytes operatorData
)
```

トークンの発行が行われ、一定量のトークンが受け取りアドレスによってオペレーターアドレスによって発行された時に発行されるイベント。

**パラメーター**
- `operator`
    - 発行を実行したアドレス。
- `to`
    - トークンを受け取るアドレス。
- `amount`
    - 発行されたトークンの数量。
- `data`
    - 受け取りアドレスに関する提供された情報。
- `operatorData`
    - オペレーターによって提供された情報。

### トークンのバーン

トークンのバーンとは、既存のトークンを廃棄する行為です。
**ERC777**では、トークンをバーンするために明示的に2つの関数（`burn`および`operatorBurn`）が定義されています。
これらの関数は、ウォレットやDAppでバーンプロセスを統合するためのものです。
ただし、トークンコントラクトは何らかの理由で一部またはすべての保有者がトークンをバーンするのを防ぐことがあります。
トークンコントラクトは他のトークンをバーンするための関数を定義することもできます。

以下のルールは、保有者のトークンをバーンする際に守るべきルールです。

- 任意の保有者アドレス（`0x0`を除く）からトークンをバーンできます。
- 総供給量はバーンされたトークンの数量だけ減少します。
- `0x0`の残高は増加しません。
- 保有者の残高はバーンされたトークンの数量だけ減少します。
- トークンコントラクトは、正しい値で`Burned`イベントを発行します（`Burned`イベントの定義に従う）。
- 保有者が**ERC1820**を介して**ERC777TokensSender**の実装を登録している場合、トークンコントラクトは保有者の`tokensToSend`フックを呼び出す必要があります。
- `operatorData`はバーンプロセス全体で不変である必要があります。
    - したがって、同じ`operatorDataはtokensToSend`フックを呼び出すためと`Burned`イベントを発行するために使用されます。

トークンをバーンする際に、以下の場合にトークンコントラクトはトランザクションを取り消す必要があります。

- オペレーターアドレスが保有者に対して許可されたオペレーターでない場合。
- バーン後の保有者の残高が、トークンコントラクトで定義された最小単位の倍数ではない場合。
- 保有者の残高がバーンするトークンの数量よりも少ない場合（つまり、保有者の残高が負の値になる場合）。
- 保有者のアドレスが`0x0`の場合。
- 保有者の`tokensToSend`フックがトランザクションを取り消す場合。

**ERC20**互換性要件
バーン時に`Sent`イベントを発行してはいけませんが、トークンコントラクトが**ERC20**対応している場合、`to`パラメータを`0x0`に設定した`Transfer`イベントを発行します。
**ERC20**標準はトークンのバーンの概念を定義していませんが、これは一般的に受け入れられているプラクティスです。

トークンコントラクトは複数の保有者のトークンを一度にバーンすることができます。
この場合以下のルールが適用されます。

- 前述のバーンルールは各保有者に適用されます。
- すべての残高の減少の合計は、合計バーン量と等しくなければなりません。
- 各保有者ごとに対応する数量の`Burned`イベントが発行されます。
- `Burned`イベントからのすべての数量の合計は、合計バーン量と等しいです。
- トークンのバーン量がゼロ（`0`）の場合、通常のバーン処理と同じように扱われます。
- データフィールドには、保有者から提供される情報が含まれており、通常の**ETH**送信トランザクションのデータフィールドと類似しています。
- `tokensToSend()`フック、`tokensReceived()`フック、または両方がこの情報を使用してトランザクションを拒否するかどうかを決定するために使用できます。
- `operatorData`フィールドは、データフィールドに類似していますが、オペレーターによって提供されます。

#### Burnedイベント

```javascript
event Burned(
    ddress indexed operator,
    address indexed from,
    uint256 amount,
    bytes data,
    bytes operatorData
);
```

バーンのプロセスにおいて、オペレーターアドレスによって指定された量のトークンが発行元アドレスからバーンされた時に発行されるイベント。

**パラメーター**
- `operator`
    - バーンを実行したアドレス。
- `from`
    - バーンされたトークンの保有者アドレス。
- `amount`
    - バーンされたトークンの数量。
- `data`
    - 保有者から提供された情報。
- `operatorData`
    - オペレーターから提供された情報。

#### burn

識別子: `fe9d9303`

```javascript
function burn(uint256 amount, bytes calldata data) external
```

`msg.sender`から指定された量のトークンをバーンする関数。

**引数**
- `amount`
    - バーンするトークンの数量。
- `data`
    - 保有者から提供された情報。

#### operatorBurn

識別子: `fc673c4f`

```javascript
function operatorBurn(
    address from,
    uint256 amount,
    bytes calldata data,
    bytes calldata operatorData
) external
```

オペレーターが`from`アドレスのトークンを指定された量だけバーンする関数。

**注意**
オペレーターアドレスがアドレス「`from`」の許可されたオペレーターでない場合、バーンプロセスは取り消されます。
オペレーターは`operatorData`を介して任意の情報を渡すことができます。
ただし、`operatorData`はオペレーターによってのみ提供される必要があります。
`from`と`msg.sender`が同じアドレスである場合があります。
つまり、アドレスは自分自身のために`operatorBurn`を呼び出すことができます。
この呼び出しは、バーンに追加してオペレーターが`operatorData`の明示的な値を指定できる点で、バーン関数と同等である必要があります。

**パラメーター**
- `from`
    - バーンするトークンの保有者アドレス。
- `amount`
    - バーンするトークンの数量。
- `data`
    - 保有者から提供された情報。
- `operatorData`
    - オペレーターから提供される情報。

### ERC777TokensSender・tokensToSendフック
**ERC777TokensSender**は、特定の保有者の残高を減少させるリクエスト（送信およびバーン）を通知するためのものです。
自分のアドレスからのトークンの減少に関する通知を受け取りたい任意のアドレス（通常のアドレスまたはコントラクト）は、以下に説明する**ERC777TokensSender**インターフェースを実装したコントラクトのアドレスを**ERC1820**を介して登録することができます。

これは、**ERC1820**レジストリ上で`setInterfaceImplementer`関数を呼び出すことによって行われます。
このとき、保有者のアドレスをアドレスとし、**ERC1820**ERC777TokensSender**ERC1820**の`keccak256`ハッシュ（`0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895`）をインターフェースハッシュとし、**ERC777TokensSender**を実装したコントラクトのアドレスを**implementer**として指定します。

**注意**
通常のアドレスは、そのアドレス自体ではなく、その代わりにコントラクトのアドレスを登録することができます。
ただし、その代わりのコントラクトのアドレスは、必ずそのコントラクト自体で以下のインターフェースを実装している必要があります。

```javascript
interface ERC777TokensSender {
    function tokensToSend(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata userData,
        bytes calldata operatorData
    ) external;
}
```

#### tokensToSend

識別子: `75ab9782`

```javascript
function tokensToSend(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes calldata userData,
    bytes calldata operatorData
) external
```

指定されたオペレーターアドレスによって、送信またはバーン（`to`が`0x0`の場合）プロセスによって、`from`アドレスから`to`アドレスにトークンの数量を送信またはバーンするリクエストを送る関数

**パラメーター**
- `operator`
    - トークンの送信、バーン処理を実行したオペレーターアドレス。
- `from`
    - トークンが送信された保有者のアドレス。
- `to`
    - 送信の場合はトークンの受信者のアドレス（バーンの場合は`0x0`）。
- `amount`
    - 保有者の残高が減少するトークンの数量。
- `data`
    - 保有者によって提供された情報。
- `operatorData`
    - オペレーターによって提供された情報。

以下のルールが`tokensToSend`フックを呼び出すときに適用されます。

- `tokensToSend`フックは、送信およびバーンプロセスに対して必ず呼び出されます。
- `tokensToSend`フックは、状態が更新される前に呼び出されます。
    - つまり、残高が減少する前に呼び出されます。
- `operator`は、送信またはバーンプロセスをトリガーしたアドレスです。
- `from`は、送信またはバーンされるトークンの保有者アドレスです。
- `to`は、送信の場合はトークンの受け取りアドレスである必要があります。
    - バーンの場合は`0x0`である必要があります。
- `amount`は、保有者が送信またはバーンしたトークンの数量です。
- `data`は、送信またはバーンプロセスに提供された追加情報（ある場合）です。
- `operatorData`は、残高の減少をトリガーしたアドレスによって提供された追加情報です。

保有者は、送信またはバーンプロセスをリバートすることでブロックすることができます（つまり、自身のアカウントからのトークンの引き出しを拒否することができます）。

**注意**
トークン保有者は、同じ**ERC777TokensSender**の実装を使用することができます。
1つのアドレスは、任意の時点で最大で1つの実装を登録できます。
したがって、**ERC777TokensSender**は異なるトークンコントラクトから呼び出される可能性があるため、その点を考慮して実装する必要があります。
`tokensToSend`呼び出しの`msg.sender`は、トークンコントラクトのアドレスであることが期待されます。

**ERC20互換性の要件**
このフックは**ERC20**より優先され、**ERC20**の`transfer`および`transferFrom`イベントを実行する前に呼び出す必要があります。
`transfer`から呼び出される場合、`operator`は`from`と同じアドレスである必要があります。
`transferFrom`から呼び出される場合、`operatorはtransferFrom`を呼び出したアドレスである必要があります。

### ERC777TokensRecipient・tokensReceivedフック
**ERC777TokensRecipient**は、特定の受け取りアドレスの残高の増加（送信およびマイント）を通知するためのものです。
自分のアドレスへのトークンのクレジットの通知を受け取りたい任意のアドレス（通常のアドレスまたはコントラクト）は、以下に説明する**ERC777TokensRecipient**インターフェースを実装したコントラクトのアドレスを**ERC1820**を介して登録することができます。

これは、**ERC1820**レジストリ上で`setInterfaceImplementer`関数を呼び出すことによって行われます。
このとき、受け取りアドレスをアドレスとし、**ERC777TokensRecipient**の`keccak256`ハッシュ（`0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b`）をインターフェースハッシュとし、**ERC777TokensRecipient**を実装したコントラクトのアドレスを`implementer`として登録します。



```javascript
interface ERC777TokensRecipient {
    function tokensReceived(
        address operator,
        address from,
        address to,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;
}
```

受け取りアドレスが**ERC777TokensRecipient**の実装を登録していないコントラクトの場合、トークンコントラクトは次の動作をします。

- もし`tokensReceived`フックが`mint`または送信呼び出しから呼ばれた場合、トークンコントラクトはリバートする必要があります。
- もし`tokensReceived`フックが**ERC20**の転送や`transferFrom`呼び出しから呼ばれた場合、トークンコントラクトはトランザクションの処理を継続します。

**注意**
通常のアドレスは、自身の代わりにインターフェースを実装しているコントラクトのアドレスを登録することができます。
ただし、コントラクトは自身のアドレスまたは他のコントラクトのアドレスを登録する必要があります。
ただし、登録されたアドレスは自身の代わりにインターフェースを実装している必要があります。


#### tokensReceived

識別子: `0023de29`

```javascript
function tokensReceived(
    address operator,
    address from,
    address to,
    uint256 amount,
    bytes calldata data,
    bytes calldata operatorData
) external
```

指定された`from`アドレスから`to`アドレスに、オペレーターアドレスによって指定されたトークン量を送信または`mint`（`from`が`0x0`の場合）するリクエストを実行する関数。
この機能は、`mint`、送信、または**ERC20**の転送プロセスの外部から呼び出すことはできません。

**パラメータ**
- `operator`
    - トークンの残高増加を実行したオペレーターアドレス（送信またはマイントを介して）。
- `from`
    - トークン保有者のアドレス（`mint`の場合は`0x0`）。
- `to`
    - トークンを受け取る受け取りアドレス。
- `amount`
    - 受け取りアドレスの残高が増加するトークンの数量。
- `data`
    - 保有者によって提供された情報。
- `operatorData`
    - オペレーターによって提供された情報。

以下のルールが`tokensReceived`フックを呼び出す時に適用されます。

- `tokensReceived`フックは、すべての送信および`mint`プロセスに対して呼び出される必要があります。
- `tokensReceived`フックは、状態が更新された後に呼び出されます。
    - つまり、残高が増加した後に呼び出されます。
- `operator`は、送信または`mint`プロセスをトリガーしたアドレスです。
- `from`は、送信の場合はトークンが送信される保有者のアドレスです。
- `from`は、`mint`の場合は`0x0`です。
- `to`は、トークンを受け取る受け取りアドレスです。
- `amount`は、受け取りアドレスが送信または`mint`したトークンの数量です。
- `data`は、送信または`mint`プロセスに提供された追加情報です。
- `operatorData`は、残高の増加をトリガーしたアドレスによって提供された追加情報です。
- トークン保有者は、送信または`mint`プロセスをリバートすることでブロックすることができます（トークンの受け取りを拒否することができます）。

**注意**
複数のトークン保有者は、同じ**ERC777TokensRecipient**の実装を使用できます。
1つのアドレスは、任意の時点で最大で1つの実装を登録できます。
したがって、**ERC777TokensRecipient**は異なるトークンコントラクトから呼び出される可能性があるため、その点を考慮して実装する必要があります。
`tokensReceived`を呼び出した`msg.sender`は、トークンコントラクトのアドレスであることが期待されます。

**ERC20互換性の要件**
このフックは**ERC20**より優先され、**ERC20**の`transfer`および`transferFrom`イベントを実行する前に呼び出す必要があります。
`transfer`から呼び出される場合、`operator`は`from`と同じアドレスである必要があります。
`transferFrom`から呼び出される場合、`operatorはtransferFrom`を呼び出したアドレスである必要があります。

## 補足

**ERC777**という規格には、いくつかの重要な理念と概念が含まれています。

### 不足点の解決と後方互換性の維持
**ERC777**は、従来のトークン規格である**ERC20**の一部の不足点を解決し、同時に**ERC20**との後方互換性を保とうとするものです。
**ERC20**の中には、トークンの送信に関する問題があったり、正確な情報の不足があったりすることがあります。
**ERC777**はこれらの課題を改善し、新しい機能を導入しつつ、既存のアプリケーションやスマートコントラクトとの連携を図ります。

### ライフサイクルの明確化
**ERC777**は、トークンのライフサイクルを明確に定義しています。
これは、トークンの生成（`mint`）、送信、破棄（`burn`）など、トークンがどのように扱われるかを詳細に規定しています。
これにより、トークンの価値や流通に関する情報の整合性が保たれます。

### データとoperatorDataの使用
トークンの`mint`、送信、バーンのプロセスでは、データと`operatorData`フィールドを使用できます。
これらのフィールドは、トークンの移動に関する情報を含むため、より多くのコンテキストを提供します。
例えば、送信者や受け取りアドレスが特定の情報をトークンの移動と共に送信することができます。

### フックの導入
**ERC777**では、トークンの送信プロセスを効率的かつ柔軟にするためにフックが導入されています。
フックは、トークンの移動プロセスに関連するアクションをカスタマイズするために使用されます。
例えば、トークンを受け取る際に特定の処理を行ったり、トークンの送信時に特定の条件をチェックしたりできます。

### 保有者のコントロールの強化
**ERC777**は、保有者がトークンの移動を制御するための仕組みを提供します。
保有者は、トークンを特定のアドレスに送信することを許可したり拒否したりすることができます。
これにより、保有者はより細かいレベルでトークンの取引を制御できます。

### ERC1820レジストリの活用
**ERC1820**レジストリを使用することで、保有者はフックを登録できます。
このレジストリは、トークンの移動に関連する処理をカスタマイズするためのコントラクトと保有者を関連付けるために使用されます。
これにより、柔軟性が高まり、異なるトークンとの統合が容易になります。

### オペレーターの概念
**ERC777**では、トークンの送信を行うためのオペレーターという概念が導入されています。
保有者は、自身のトークンを他のアドレスに移動させるためのオペレーターを指定できます。
また、保有者はオペレーターを任意に取り消すこともできます。

## 後方互換性

このEIPによる**ERC777**規格は、従来の**ERC20**トークン規格との後方互換性を保ちつつ、新たな機能と柔軟性を導入しています。

### 送信と操作の名称
**ERC777**では、トークンの送信には`send`と`operatorSend`が使われます。
これは、従来の**ERC20**では`transfer`と`transferFrom`が使われていました。
これにより、どのトークン規格が使用されているかの混乱を避けることができます。

### ERC20関数の実装
**ERC777**では、トークンが完全に**ERC20**と互換性があるように、`transfer`、`transferFrom`、`approve`、`allowance`などの**ERC20**関数の実装を許可しています。
これにより、既存の**ERC20**関数を使用しながら、新しい機能を追加できます。

### 小数点の実装
**ERC777**では、トークンの小数点以下の桁数を示す`decimals()`関数を実装できます。
これは、**ERC20**との後方互換性を維持するためのオプションです。
実装される場合、常に`18`を返す必要があります。

### 同時実装
トークンコントラクトは、**ERC20**と**ERC777**を同時に実装できます。
`name`、`symbol`、`balanceOf`、`totalSupply`などのビュー関数や内部データの実装は、両方の規格で共有できます。
ただし、**ERC777**には必須の関数（`name`、`symbol`、`balanceOf`、`totalSupply`）がありますが、`decimals`は**ERC777**の規格には含まれていません。

### フックの優先順位
新たに**ERC20**関数を実装する場合でも、`tokensToSend`と`tokensReceived`フックは**ERC20**関数よりも優先されます。
したがって、**ERC20**の`transfer`や`transferFrom`を呼び出している場合でも、トークンコントラクトは**ERC1820**を通じて`from`アドレスと`to`アドレスがそれぞれ`tokensToSend`と`tokensReceived`フックを実装しているかどうかを確認します。
いずれかのフックが実装されている場合、必ずそれを呼び出します。
なお、コントラクトに`tokensReceived`フックが実装されていない場合でも、`transfer`呼び出しは受け入れられるべきですが、この場合、トークンがロックされる可能性が高くなります。

### イベントの発行
**ERC777**規格では、トークンの送信、`mint`、バーンの際には`Sent`、`Minted`、`Burned`イベントが必ず発行されます。
また、トークンコントラクトが**ERC20**Tokenインターフェースを**ERC1820**を介して実装している場合、`mint`とバーンの際には`Transfer`イベントを発行します。
送信の際には（**ERC20**の標準で定義されている通り）必ず`Transfer`イベントを発行します。
したがって、トークンの移動に関しては、**ERC20**の`Transfer`イベントと**ERC777**の`Sent`、`Minted`、`Burned`イベントが共に発行されることがあります。
ただし、開発者はこれらのイベントを別々の移動として考えず、それぞれの規格に基づいて適切に扱う必要があります。

## テストと参考実装

以下のGithubリポジトリに格納されています。

https://github.com/0xjac/ERC777

## 引用

Jacques Dafflon <mail@0xjac.com>, Jordi Baylina <jordi@baylina.cat>, Thomas Shababi <tom@truelevel.io>, "ERC-777: Token Standard," Ethereum Improvement Proposals, no. 777, November 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-777.

## 最後に

今回は「**ERC20**規格と互換性を持ちながら、トークンコントラクトの標準インターフェースと動作を提案している**ERC777**」についてまとめてきました！
いかがだったでしょうか？

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

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