TetherTokenをプログラムから解読する

# はじめに

ERC20トークンについて大体理解した人向け

まだ理解できていない人はこちらで 
[ERC20トークンでマイトークン作成・実行までまるっと解説！](https://qiita.com/sinsinpurin/items/e95f7e167b3116d29c68)

2020年7月11日現在、最も流通量の多いTetherTokenをEtherScanからコントラクトを解読していこう
<img width="1254" alt="スクリーンショット 2020-07-11 16.57.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382669/83fea1db-bd74-e362-3337-ae01465857ac.png">



# TetherTokenとは

Tether社が2015年2月から発行している、米ドルとペッグしたステーブルトークン。ドルと連動し価格が決定されている。
イーサリアム上のERC20トークン規格に準拠していて、ERC20トークンの中では最も流通量の多いトークンである。
Tether社の保有する資産分、トークンを発行することでドルと紐付けている。1ドル≒1USDTとなるように設定されている。
そのため、他の暗号資産(ステーブルではない)よりTetherは価格変動が少ない。
他に比べ、BlackListを作成できるため中央集権度合いが高い。

# Tether社って？
[Tether社 ホームページ](https://tether.to/)

<img width="1266" alt="スクリーンショット 2020-07-11 17.03.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382669/a02197c6-0cbd-22ee-dec7-901629088ee2.png">

## Tetherがステーブルトークンを実現した方法

資産量(ドル)とUSDTを同じ量にすることで`1$=1USDT`を実現しようとしてる。
<img width="461" alt="スクリーンショット 2020-07-13 19.59.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382669/542cc82a-cf22-6231-5fac-cfce8c3a34a1.png">
確かに2020/7/13現在の資産量とUSDTの総発行量は同じになっている。
[Tether社の資産](https://wallet.tether.to/transparency)

# TetherTokenコントラクト解読
間違ってたらすまぬ
![Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/382669/20f6ca3c-50df-51d6-26cc-ad4c88727224.png)

## コントラクト

#### ERC20Basic

トークンの最低限の処理を定義。transfer,totalSupply,balanceOf定義。処理は未定義。


#### ERC20

ERC20の特徴であるaprove,transferFrom関数を定義。処理は未定義。

#### Ownable

所有者のみ(Owner)が動かせる関数修飾子を定義。

#### BasicToken

ERC20Basicの処理を定義。
basisPointRateは取引手数料を後から追加するとき用の変数

```js
    function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
        // 取引手数料
        uint fee = (_value.mul(basisPointsRate)).div(10000);
        if (fee > maximumFee) {
            fee = maximumFee;
        }
        uint sendAmount = _value.sub(fee);
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(sendAmount);
        if (fee > 0) {
            balances[owner] = balances[owner].add(fee);
            Transfer(msg.sender, owner, fee);
        }
        Transfer(msg.sender, _to, sendAmount);
    }
```

#### Pausable

コントラクトの動作(トークンの送信)を停止させることができる関数を定義。

#### StandardToken

ERC20のaprove,transferFrom関数の処理を定義。

```js

    /**
    * @dev Transfer tokens from one address to another
    * @param _from address The address which you want to send tokens from
    * @param _to address The address which you want to transfer to
    * @param _value uint the amount of tokens to be transferred
    */
    function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
        var _allowance = allowed[_from][msg.sender];

        // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
        // if (_value > _allowance) throw;

        uint fee = (_value.mul(basisPointsRate)).div(10000);
        if (fee > maximumFee) {
            fee = maximumFee;
        }
        if (_allowance < MAX_UINT) {
            allowed[_from][msg.sender] = _allowance.sub(_value);
        }
        uint sendAmount = _value.sub(fee);
        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(sendAmount);
        if (fee > 0) {
            balances[owner] = balances[owner].add(fee);
            Transfer(_from, owner, fee);
        }
        Transfer(_from, _to, sendAmount);
    }

```

#### BlackList

アカウントを凍結するリスト

#### UpgradedStandardToken



#### TetherToken

テザートークンのメイン処理。

##### [function deprecated]
アップグレードが必要な場合はdeprecatedをtrueにし、upgradeしたアドレスをいれる。

```js

    // deprecate current contract in favour of a new one
    function deprecate(address _upgradedAddress) public onlyOwner {
        deprecated = true;
        upgradedAddress = _upgradedAddress;
        Deprecate(_upgradedAddress);
    }
```

##### [function transfer]

```js

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function transfer(address _to, uint _value) public whenNotPaused {
        require(!isBlackListed[msg.sender]);
        if (deprecated) {
            return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
        } else {
            return super.transfer(_to, _value);
        }
    }
```

##### [function issue]
ownerアドレスに新規トークンを発行する。(ownerのみ)

```js

    // Issue a new amount of tokens
    // these tokens are deposited into the owner address
    //
    // @param _amount Number of tokens to be issued
    function issue(uint amount) public onlyOwner {
        require(_totalSupply + amount > _totalSupply);
        require(balances[owner] + amount > balances[owner]);

        balances[owner] += amount;
        _totalSupply += amount;
        Issue(amount);
    }
```

##### [function redeem]
ownerのもつトークンを焼却する。(ownerのみ)

```js
    // Redeem tokens.
    // These tokens are withdrawn from the owner address
    // if the balance must be enough to cover the redeem
    // or the call will fail.
    // @param _amount Number of tokens to be issued
    function redeem(uint amount) public onlyOwner {
        require(_totalSupply >= amount);
        require(balances[owner] >= amount);

        _totalSupply -= amount;
        balances[owner] -= amount;
        Redeem(amount);
    }


```

##### [function setParams]
取引手数料の割合と最大取引手数料を設定(ownerのみ)

```js

    function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
        // Ensure transparency by hardcoding limit beyond which fees can never be added
        require(newBasisPoints < 20);
        require(newMaxFee < 50);

        basisPointsRate = newBasisPoints;
        maximumFee = newMaxFee.mul(10**decimals);

        Params(basisPointsRate, maximumFee);
    }
```

# UpgradeStandardToken

TetherToken Contractの更新が必要な場合はTetherToken以外のコントラクトを使い回すために
コントラクトアドレスを参照して残す。
所有者のデータは使い回す必要がある。(upgradeの度にゼロに戻ってしまうから)

```js

    function deprecate(address _upgradedAddress) public onlyOwner {
        deprecated = true;
        upgradedAddress = _upgradedAddress;
        Deprecate(_upgradedAddress);
    }
```


```js

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function transfer(address _to, uint _value) public whenNotPaused {
        require(!isBlackListed[msg.sender]);
      // もしアップデートが必要なのであればアップグレード元のアドレスを引数にdeplecate関数を実行する
        if (deprecated) {
                // UpgradedStandardTokenのアドレスを指定してtransferByLegacyを実行
            return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
        } else {
            return super.transfer(_to, _value);
        }
    }

```

# Short Address Attack(sol v0.5.0以前)
アドレスの後続に0があり、意図的に0を省略した場合、solidityコンパイラ(v0.5.0以前)によって0が補填されるが、次の引数に補填分が論理シフトしてしまう。
solidity v0.5.0に、この脆弱性はcalldataのサイズが間違っている元のサイズに戻す対策された。

v0.5.0以前のコードは以下の関数修飾子を追加し対策できる。

```js

// @dev Fix for the ERC20 short address attack
modifier onlyPayloadSize(uint size){
    require(!(msg.data.length < size + 4));
    _;
}
```

# わからなかったところ

transferByLegacy(msg.sender, _to, _value)はtransferの処理と紐づいていると思われるけど、どの部分で書かれているかがわからなかった。


# TetherTokenコントラクト

[TetherToken EtherScan](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code)