USDTのコントラクトを呼ぶ

# はじめに（Introduction）

[前回](https://qiita.com/tis-nakagawa/items/577d85d571a13d7c3bc2)、USDTのコントラクトを読んでみました。
ERC20の他に以下の機能を有していることがわかりました。

- 認証（Ownable）
- 手数料（Fee）
- 一時停止（Pausable）
- アップグレード（Upgrade）
- ブラックリスト（BlackList）
- 発行（Issue）/ 償還（Redeem）

実際にどのような状況であるかをブロックチェーンから呼び出してみたいと思います。

# 準備

コントラクトアドレスは`0xdAC17F958D2ee523a2206206994597C13D831ec7`です。
コントラクトが作成された、トランザクションハッシュは`0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190`です。
生成されたブロックは`4634748`です。

https://etherscan.io/tx/0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190

RPCのエンドポイントは以下を使用します。

https://www.publicnode.com/

```javascript
const { ethers } = require("ethers");

const USDT_ABI = [
    // https://ethereum.org/ja/developers/docs/standards/tokens/erc-20/
    'function name() public view returns (string)',
    'function symbol() public view returns (string)',
    'function decimals() public view returns (uint8)',
    'function totalSupply() public view returns (uint256)',
    'function balanceOf(address _owner) public view returns (uint256 balance)',
    'function transfer(address _to, uint256 _value) public returns (bool success)',
    'function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)',
    'function approve(address _spender, uint256 _value) public returns (bool success)',
    'function allowance(address _owner, address _spender) public view returns (uint256 remaining)',
    'event Transfer(address indexed _from, address indexed _to, uint256 _value)',
    'event Approval(address indexed _owner, address indexed _spender, uint256 _value)',
    // Ownable
    'function owner() public view returns (address)',
    // fee
    'function basisPointsRate() public view returns (uint256)',
    'function maximumFee() public view returns (uint256)',
    'event Params(uint feeBasisPoints, uint maxFee)',
    // Pausable
    'function paused() public view returns (bool)',
    'event Pause()',
    'event Unpause()',
    // upgrade
    'function deprecated() public view returns (bool)',
    'event Deprecate(address newAddress)',
    // BlackList
    'function getBlackListStatus(address _maker) public view returns (bool)',
    'event DestroyedBlackFunds(address _blackListedUser, uint _balance)',
    'event AddedBlackList(address _user)',
    'event RemovedBlackList(address _user)',
    // Issue
    'event Issue(uint amount)',
    // Redeem
    'event Redeem(uint amount)',
];

// https://www.publicnode.com/
const ETHEREUM_RPC_ENDPOINT = 'https://ethereum-rpc.publicnode.com';
const MAX_BLOCK_RANGE = 50000;

// https://etherscan.io/tx/0x2f1c5c2b44f771e942a8506148e256f94f1a464babc938ae0690c6e34cd79190
const TETHER_TOKEN_ADDRES = '0xdAC17F958D2ee523a2206206994597C13D831ec7';
const CREATE_BLOCK_NUMBER = 4634748;

async function main() {
    const provider = new ethers.JsonRpcProvider(ETHEREUM_RPC_ENDPOINT);
    const tether = new ethers.Contract(TETHER_TOKEN_ADDRES, USDT_ABI, provider);
    const blockNumber = await provider.getBlockNumber();
    console.log(`blockNumber     : ${blockNumber}`);

    // ここにコードを記述します。

}

main().then(() => {
    process.exit(0);
}).catch((error) => {
    console.error(error);
    process.exit(1);
});
```

# ERC20

ERC20の基本的な情報を見てみます。

```javascript
    // ERC20
    const name = await tether.name();
    const symbol = await tether.symbol();
    const decimals = await tether.decimals();
    const totalSupply = await tether.totalSupply();
    console.log(`name            : ${name}`);
    console.log(`symbol          : ${symbol}`);
    console.log(`decimals        : ${decimals}`);
    console.log(`totalSupply     : ${ethers.formatUnits(totalSupply, decimals)}`);
```

結果は以下のとおりです。

```powershell
blockNumber     : 20939907
name            : Tether USD
symbol          : USDT
decimals        : 6
totalSupply     : 54966490253.66394
```

54,966,490,253 USDT！？すごいですね、日本円して8兆円規模のトークンのようです。
コントラクトの作成時は、100 USDTだったのでかなりの規模に成長しているようです。

# 認証（Ownable）

現在の`owner`を見てみます。
また、現在USDTをどのくらい持っているかも見てみます。

```javascript
    // Ownable
    const owner = await tether.owner();
    const balanceOf = await tether.balanceOf(owner);
    console.log(`owner           : ${owner}`);
    console.log(`balanceOf       : ${ethers.formatUnits(balanceOf, decimals)}`);
```

結果は以下のとおりです。

```powershell
blockNumber     : 20940260
owner           : 0xC6CDE7C39eB2f0F0095F41570af89eFC2C1Ea828
balanceOf       : 108983.76699
```

現在の`owner`は`0xC6CDE7C39eB2f0F0095F41570af89eFC2C1Ea828`だそうです。
[Ethscan](https://etherscan.io/address/0xC6CDE7C39eB2f0F0095F41570af89eFC2C1Ea828#code)によれば`MultiSigWallet`だそうです。
コントラクトの作成とは異なるので、[`transferOwnership`](https://etherscan.io/tx/0x549905bd7474a3d4aa582205a99a35aba6a94fdd37e08904c6a836df1af28673)を利用して変更しています。

# 手数料（Fee）

現在の手数料関連のパラメータを見てみます。

```javascript
    // Fee
    const basisPointsRate = await tether.basisPointsRate();
    const maximumFee = await tether.maximumFee();
    console.log(`basisPointsRate : ${basisPointsRate}`);
    console.log(`maximumFee      : ${maximumFee}`);
```

結果は以下のとおりです。

```powershell
blockNumber     : 20940460
basisPointsRate : 0
maximumFee      : 0
```

手数料は設定されていないようです。
イベント検索でパラメータ設定時に発行される`Params`イベントを検索してみましたが検出できなかったので、手数料が発生していることはいままでに一度もなさそうです。

# 一時停止（Pausable）

一時停止の状況を取得します。

```javascript
    // Pausable
    const paused = await tether.paused();
    console.log(`paused          : ${paused}`);
```

結果は以下のとおりです。

```powershell
blockNumber     : 20940586
paused          : false
```

現在稼働中のトークンなので停止していることはないです。
イベント検索で`Pause`、`Unpause`を検索してみたところ検出されませんでした、停止されたことは今までに一度もなさそうです。

# アップグレード（Upgrade）

アップグレードされているかを見てみます。

```javascript
    // Upgrade
    const deprecated = await tether.deprecated();
    console.log(`deprecated      : ${deprecated}`);
```

結果は以下のとおりです。

```powershell
blockNumber     : 20940787
deprecated      : false
```

アップグレードされていないようです。
`deprecated`を`true`に変更するとコード的には`false`に変更することはできないため、今までに一度もアップグレードはされていないようです。
`Deprecate`イベントも検出されませんでした。

# ブラックリスト（BlackList）

ブロック番号`20940838`の時点でのイベントを収集してみます。
イベント`AddedBlackList`は、ブラックリストへ追加した時に発生するイベントです。
イベント`DestroyedBlackFunds`は、ブラックリストに入っているアドレスの資産を破棄した時に発生するイベントです。
イベント`RemovedBlackList`は、ブラックリストから除外する時に発生するイベントです。

イベントの回数は以下のとおりです。


|イベント名|回数|総量|
|:--|--:|--:|
|AddedBlackList|1,828|-|
|DestroyedBlackFunds|853|243,627,751.978060|
|RemovedBlackList|66|-|

これらの操作は`owner`だけが出来る操作です。

# 発行（Issue）/ 償還（Redeem）

ブロック番号`20940978`の時点でのイベントを収集してみます。
イベント`Issue`は、USDTを発行した時に発生するイベントです。
イベント`Redeem`は、USDTを償還した時に発生するイベントです。

|イベント名|回数|総量|
|:--|--:|--:|
|Issue|180|64,710,018,005.642000|
|Redeem|3|9,500,000,000.000000|

これらの操作は`owner`だけが出来る操作です。

# まとめ（Conclusion）

USDT（Tether）は、７年前から運用されている老舗のトークンですが今では販売所などで取引できるトークンです。
トークンに必要な機能も備えているようです。
手数料や一時停止、アップグレードなどの機能は使われていないようですが、機能として想定していることがわかりました。
今後発行されるERC20の参考となると思います。

# 補足（Appendix）

## イベント検索（Search Event）

対象のイベントにあるコメントを外して使用してください。

```javascript
    console.log('CREATE_BLOCK_NUMBER', CREATE_BLOCK_NUMBER, 'blockNumber', blockNumber);
    let eventFilter = [[]];
    // Pause
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Pause().getTopicFilter());
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Unpause().getTopicFilter());
    // Fee
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Params().getTopicFilter());
    // Update
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Deprecate().getTopicFilter());
    // BlackList
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.DestroyedBlackFunds().getTopicFilter());
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.AddedBlackList().getTopicFilter());
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.RemovedBlackList().getTopicFilter());
    // Issue / Redeem
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Issue().getTopicFilter());
    // eventFilter[0] = eventFilter[0].concat(await tether.filters.Redeem().getTopicFilter());
    if (eventFilter[0].length > 0) {
        let eventCount = 0;
        for (let i = CREATE_BLOCK_NUMBER; i < blockNumber; i += MAX_BLOCK_RANGE + 1) {
            const from = i;
            const to = from + MAX_BLOCK_RANGE < blockNumber ? from + MAX_BLOCK_RANGE : blockNumber;
            console.log('queryFilter', from, to);
            const logs = await tether.queryFilter(eventFilter, from, to);
            if (logs.length > 0) {
                for (let log of logs) {
                    console.log('event', log.transactionHash, log.fragment.name, log.args.join(' '));
                }
                eventCount += logs.length;
            }
        }
        console.log('event count :', eventCount);
    }

```