EOSIOで独自の仮想通貨トークンを発行・送金してみる

開発環境の構築とかはこちら参照：
https://qiita.com/YukiSekiguchi/items/623359857269aff74586
https://qiita.com/YukiSekiguchi/items/f84afbf35f4fa039859c

# 今回やること
https://developers.eos.io/eosio-home/docs/token-contract に沿ってEOSIOのプライベートネットワーク上で仮想通貨トークンを発行、送金してみる

# 準備
nodeosのコンテナが動いてないようなら動かす

```
$docker start 1574a37de3c6
```

まずはウォレットをopenする+unlockする。

```
$cleos wallet open -n default
$cleos wallet unlock -n default
```

トークン発行するスマートコントラクト用にアカウントを作成。開発用の鍵を使う。

```
$cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV
```

トークン発行用のスマートコントラクトの実装は公式が提供してくれているので、これを持ってくる

```
$git clone https://github.com/EOSIO/eosio.contracts
```

そのままコンパイルするだけ。簡単。

```
$cd eosio.contracts
$eosio-cpp -o eosio.token.wasm src/eosio.token.cpp -I include/
```

# トークンの作成
コンテナの中にeosio.token.wasmとabiを持っていったら

```
$ cleos set contract eosio.token <eosio.token.wasmがおいてあるコンテナ内のパス> --abi <eosio.token.abiのおいてあるコンテナ内のパス> -p eosio.token@active
```

とすると

```
Reading WASM from /home/eosio.contracts/eosio.token/eosio.token.wasm...
Publishing contract...
executed transaction: c12511f41efb3ec72e64a9ab8ce040f2c65c54a7d7d21ef847bb758b0b49e79c  9320 bytes  1490 us
#         eosio <= eosio::setcode               {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d0100000001b0011d60037f7e7f0060047f...
#         eosio <= eosio::setabi                {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65...
2018-10-08T12:07:31.800 thread-0   main.cpp:481                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet
```

と出てスマートコントラクトがネットワーク上にデプロイされる。
この状態で次のコマンドを打つとトークンが作成できる。

```
cleos push action eosio.token create '[ "eosio", "1000000000.0000 HOGE"]' -p eosio.token@active
```

`1000000000.0000`は発行上限が`1000000000`で、最小単位が`0.0001`になること表す。
また`HOGE`の部分は任意の大文字アルファベットを入れる。これがトークンの単位になる。普通は3文字にする(と思う)。
今回はこのまま`HOGE`コインということで進める。

# トークンの発行
発行(issue)と作成(create)は違う概念で、createはトークンの上限と最小単位と単位を規定し、実際のトークンはeosio.token自身が持つ。
トークンを通常のアカウント向けに渡すのが発行である(この辺の理解は怪しい)。

前回作った`alice`と`bob`にトークンを発行し、やりとりさせてみる。
まず`alice`に`100.0000 HOGE`発行する。

```
$ cleos push action eosio.token issue '[ "alice", "100.0000 HOGE", "memo" ]' -p eosio@active
executed transaction: deafe9a28bbd20697c28f1e2a2b7c2997d589115c661d5010cbe25a909074460  128 bytes  6136 us
#   eosio.token <= eosio.token::issue           {"to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#   eosio.token <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#         eosio <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#         alice <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
```
`memo`の部分は文字通りメモで、取引について好きなメモを残せる。

なお`HOGE`コインの最小単位は0.0001にしたので、きっちり.0000まで書かないと以下のようにエラーになる。

```
$ cleos push action eosio.token issue '[ "alice", "100.0 HOGE", "memo" ]' -p eosio@active
Error 3050003: eosio_assert_message assertion failure
```

また、`-d`をつけるとdry runができる(見てもよくわからない)。

```
$ cleos_d push action eosio.token issue '[ "alice", "100.0000 HOGE", "memo" ]' -p eosio@active -d
{
  "expiration": "2018-10-08T12:41:28",
  "ref_block_num": 52702,
  "ref_block_prefix": 2512073667,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio.token",
      "name": "issue",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000855c3440420f000000000004484f4745000000046d656d6f"
    }
  ],
  "transaction_extensions": [],
  "signatures": [
    "SIG_K1_KexcdCmckuWp9bVBKeiLNDuLQA1L4Jenicy44TBTFxajF9dFrpAutrjRd4gvZdWrA8VA8ftrZ5qvEAF9FtDAYPHLpJPv2U"
  ],
  "context_free_data": []
}
```
アカウントが持つトークンは次のように確認できる

```
$ cleos get currency balance eosio.token alice HOGE
100.0000 HOGE
```

# トークンの送金
これで`alice`は`100.0000HOGE`持っているので、`bob`に送金してみる。
試しにmemoに日本語を入れたけど大丈夫だった(途中で切れてしまったが)。

```
$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 HOGE", "アリスからボブに25.0000HOGE送金する" ]' -p alice@active
executed transaction: 8378a1d8577444796660e9ae5f417f71ad05bcfb54fa4f1e82ed4f65dacd1d5e  176 bytes  2931 us
#   eosio.token <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
#         alice <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
#           bob <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
2018-10-08T12:45:34.574 thread-0   main.cpp:481                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet
```
`bob`のトークン所有量を見てみると、

```
$cleos get currency balance eosio.token bob HOGE
25.0000 HOGE
```
となり、`alice`のトークン所有量を見てみると、

```
$cleos get currency balance eosio.token alice HOGE
75.0000 HOGE
```
となっていて、`alice`から`bob`に`25.0000 HOGE`送金できたことがわかる。

ちなみに試しに`bob`が勝手に`alice`から`bob`に送金しようとする(最後を`bob@active`に変える)と何が起きるかというと

```
$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 HOGE", "アリスからボブに勝手に25.0000HOGE送金する" ]' -p bob@active
Error 3090004: Missing required authority
```
となり、勝手なことはできないようになっている。

これは`src/eosio.token.cpp`の中身を見てみると

```
void token::transfer( account_name from,
                      account_name to,
                      asset        quantity,
                      string       memo )
{
    eosio_assert( from != to, "cannot transfer to self" );
    require_auth( from );
    eosio_assert( is_account( to ), "to account does not exist");
    auto sym = quantity.symbol.name();
    stats statstable( _self, sym );
    const auto& st = statstable.get( sym );
︙
︙
```
となっており、前回もやった`require_auth( from )`が効いているからであることがわかる。