ERC20トークンの構造を理解する

Dapps開発の際に調査したので備忘録としてシェア。

# 背景
Ethereumの決済予約ができるスマートコントラクトを作ったのだが、ERC20にも対応させようと思って調べていたら、意外と日本では情報発信されてなかったので備忘録がわりにシェアします。

# ERC20を実装するには
ERC20トークンは[OpenZeppelin]("https://openzeppelin.org/")を使えば比較的簡単に実装することができます。具体的な実装方法は[こちら]("https://qiita.com/kyrieleison/items/a5c049097c165cd792bf")の記事などを参考にしていただければと思います。

# ERC20の構造
<img width="829" alt="Screen Shot 2018-11-17 at 14.36.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/308845/1e54b976-aef8-6395-be80-062cfc2bba37.png">
ERC20の主な内部構造は以下のようになっています。
* データ格納テーブル2つ
* 送金用関数2つ

### データ格納用テーブル
* <strong>_balancesテーブル</strong><br/>
残高情報格納用テーブル。所有者のethereumアドレスと、そのアドレスの残高を記録しているテーブルです。
* <strong>_allowedテーブル</strong><br/>
送金許可フラグを格納するテーブル。送金オペレーションを自分以外の人に委託する場合に使用します。「送金先」と「送金額」を登録して、送金を自分以外の人に委託できるようになります。

```JavaScript
pragma solidity ^0.4.24;

import "./IERC20.sol"; // 雛形のコントラクトを読み込み
import "../../math/SafeMath.sol"; // 算術演算処理を集めたコントラクトを読み込み


contract ERC20 is IERC20 {
  using SafeMath for uint256;
  // _balancesテーブルの定義
  mapping (address => uint256) private _balances;
  // _allowedテーブルの定義
  mapping (address => mapping (address => uint256)) private _allowed;
```

### 送金関連の関数
* <strong>transfer関数</strong><br/>自分のアドレスから他人のアドレスにトークンを送金する関数（送金操作をするのは自分）
    * 入力値：
        * <strong>to</strong> (宛先アドレス address型)
        * <strong>value</strong> (送金金額 uint256型)
        * （送信アドレスは自分のアドレスで固定)
* <strong>transferFrom関数</strong><br/>自分のアドレスから他人のアドレスにトークンを送金する関数（送金操作をするのは自分以外でも可能）
    * 入力値：
        * <strong>from</strong> (送信アドレス address型)
        * <strong>to</strong> (宛先アドレス address型)
        * <strong>value</strong> (送金金額 uint256型)


#### transfer関数
_balancesテーブルの残高を変更することによって、資金移動を表現します。
<img width="684" alt="Screen Shot 2018-11-17 at 13.45.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/308845/8d0760df-ab7a-ee82-0f05-7c8fd3f99567.png">

```JavaScript
// transfer（自分から他人のアドレスにトークンを送金する関数）
function transfer(address to, uint256 value) public returns (bool) {
    // _transferを実行
    _transfer(msg.sender, to, value);
    return true;
}

function _transfer(address from, address to, uint256 value) internal {
    require(to != address(0));   // 送信先アドレスが実在する必要がある

    _balances[from] = _balances[from].sub(value);   // 自分のアドレスの残高 - value
    _balances[to] = _balances[to].add(value);   // 相手のアドレスの残高 + value
    emit Transfer(from, to, value);
}
```

#### transferFrom関数
以下の３段階でトークンを送金します。

* 送金許可テーブル（未承認）作成
* 送金許可テーブルを承認
* 送金処理を実行


##### 1. 送金許可テーブル（未承認）作成
送信アドレスには自分以外のアドレスを指定可能。ただし、実際に送金をするには送信アドレスの所有者が承認する必要がある。
<img width="863" alt="Screen Shot 2018-11-17 at 14.22.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/308845/a2d22a34-9cc8-42a3-556c-3b18619b6a33.png">


```JavaScript
// 事前処理①：送金許可テーブルを作る（実行者以外のアドレスを送信アドレスに指定可能）
function allowance(
    address owner,  // 送信アドレス
    address spender // 受信アドレス
   )
    public
    view
    returns (uint256)
  {
    return _allowed[owner][spender];   // 送金許可テーブル（未承認）を作成
}
```
##### 2. 送金許可テーブルを承認する
送信者のみが送金許可テーブルにアクセスでき、承認処理を実行できる。（この段階ではまだコインの移動は行われない）
<img width="865" alt="Screen Shot 2018-11-17 at 14.26.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/308845/0ac4306a-a2c5-c872-ff2f-dbe8dee66e52.png">

```JavaScript
// 事前処理②：送信者のみが承認できる
function approve(address spender, uint256 value) public returns (bool) {
    require(spender != address(0));

    _allowed[msg.sender][spender] = value; // 送信者のみが送金許可テーブルにアクセス可能
    // 金額が入力された送金許可テーブル = 許可済み　とみなす
    emit Approval(msg.sender, spender, value);
    return true;
}
```

##### 3. 送金処理を実行する
送金許可が承認されていれば誰でも送金処理を実行できる。（この段階でコインが移動する）
<img width="880" alt="Screen Shot 2018-11-17 at 14.28.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/308845/9319b7f4-1bbe-562e-a10c-096c98a06eb4.png">


```JavaScript
// 送金処理：_allowedテーブルを元に、送金処理を実行する
function transferFrom(
    address from,   // 送信アドレス
    address to,     // 受信アドレス
    uint256 value   // 送金金額
  )
    public
    returns (bool)
  {
    _allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);   // 送金許可テーブルの内容を更新
    _transfer(from, to, value);   // 送金処理を実行
    return true;
}
```