はじめに
本連載ではEOS Developer Portalで紹介されているeosio.tokenコントラクトを用いて、コントラクトのデプロイや実行を行い、より実用的なコントラクトの構造や実行方法を学びます。
本記事では、前回作成したSYSトークンを用いて、eosio.tokenコントラクトの残りのアクションの動作確認をおこないます。
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
関連する記事
EOSIO Developer Portal 解説 最短でHello World!
EOSIO Developer Portal 解説 独自トークンの発行 第1回
EOSIO Developer Portal 解説 独自トークンの発行 第2回
EOSIO Developer Portal 解説 独自トークンの発行 第3回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第1回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第2回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第3回
issueアクションによるトークン発行
前回の記事でSYSトークンを作成しましたが、まだ流通量(supply)は0のままでした。まずは、作成したSYSトークンを新規に発行して、流通量を増やしてみましょう。トークンの新規発行には、issueアクションを実行します。
eosio.token.cppに記載されているissueアクションの実装をコード1に抜粋します。eosio.token.cpp全体のコードは、前回ダウンロードしたソースコードか、以下のURLで確認できます。
https://github.com/EOSIO/eosio.contracts/blob/master/eosio.token/src/eosio.token.cpp
コード1. eosio.token.cppのissueアクション実装抜粋
void token::issue( name to, asset quantity, string memo )
{
auto sym = quantity.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
stats statstable( _self, sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
eosio_assert( existing != statstable.end(), "token with symbol does not exist, create token before issue" );
const auto& st = *existing;
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must issue positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
statstable.modify( st, same_payer, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( *this, transfer, { {st.issuer, "active"_n} },
{ st.issuer, to, quantity, memo }
);
}
}
issueアクションは、引数として発行したトークンを付与するアカウント、発行するトークン、メモの文字列をとります。issueアクションは、あらかじめトークンに設定されているissuerしか実行できません。今回の SYS トークンの場合、issuer は eosio アカウントになります。
aliceに対して 100.0000 SYS トークンを発行する例をコマンド1に示します。アクションを実行するアカウントは、-pオプションで指定します。
コマンド1. aliceに対して 100.0000 SYS トークンを発行する
$ cleos push action eosio.token issue '[ "alice", "100.0000 SYS", "memo" ]' -p eosio@active
executed transaction: 822a607a9196112831ecc2dc14ffb1722634f1749f3ac18b73ffacd41160b019 268 bytes 1000 cycles
# eosio.token <= eosio.token::issue {"to":"user","quantity":"100.0000 SYS","memo":"memo"}
>> issue
# eosio.token <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
>> transfer
# eosio <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
# user <= eosio.token::transfer {"from":"eosio","to":"user","quantity":"100.0000 SYS","memo":"memo"}
正常に100.0000 SYSトークンが発行されているかどうか、下記コマンド2を実行してsupplyの値を確認してみます。
コマンド2. SYSトークンのstatを確認する
$ cleos get table eosio.token SYS stat
{
"rows": [{
"supply": "100.0000 SYS",
"max_supply": "1000000000.0000 SYS",
"issuer": "eosio"
}
],
"more": false
}
続いて、正しくaliceの残高が更新されているかをコマンド3を用いて確認してみます。
コマンド3. aliceの残高を確認する
$ cleos get table eosio.token alice accounts
{
"rows": [{
"balance": "100.0000 SYS"
}
],
"more": false
}
上記コマンドから、正しくaliceに100.0000 SYSトークンが発行されていることが分かります。
transferアクションによるトークン送付
続いて、aliceの保有しているトークンを、bobに送るアクションを試してみましょう。トークンの送付には、transferアクションを用います。
コード2に、transferアクションの実装コード抜粋を示します。
コード2. eosio.token.cppのtransferアクション実装抜粋
void token::transfer( name from,
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.code();
stats statstable( _self, sym.raw() );
const auto& st = statstable.get( sym.raw() );
require_recipient( from );
require_recipient( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
auto payer = has_auth( to ) ? to : from;
sub_balance( from, quantity );
add_balance( to, quantity, payer );
}
transferアクションは、引数に送付元と送付先のアカウント、送付するトークン、メモの文字列を取ります。送付元に指定されたアカウントのみがtransferアクションを実行できます。
aliceからbobに25.0000 SYSトークンを送付する例をコマンド4に示します。
コマンド4. aliceからbobに25.0000 SYSトークンを送付する
$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 SYS", "m" ]' -p alice@active
executed transaction: 06d0a99652c11637230d08a207520bf38066b8817ef7cafaab2f0344aafd7018 268 bytes 1000 cycles
# eosio.token <= eosio.token::transfer {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"Here you go bob!"}
>> transfer
# user <= eosio.token::transfer {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"Here you go bob!"}
# tester <= eosio.token::transfer {"from":"alice","to":"bob","quantity":"25.0000 SYS","memo":"Here you go bob!"}
トークンが正しく送付されたか、aliceの残高を確認してみましょう。
コマンド5. aliceの残高を確認する
$ cleos get table eosio.token alice accounts
{
"rows": [{
"balance": "75.0000 SYS"
}
],
"more": false
}
もともと 100.0000 SYSあった残高が、75.0000 SYSに減少していることが分かります。
続いて、bobの残高も確認してみましょう。
コマンド6. bobの残高を確認する
$ cleos get table eosio.token bob accounts
{
"rows": [{
"balance": "25.0000 SYS"
}
],
"more": false
}
bobの残高が25.0000 SYSとなり、正しくトークンが送付されていることが分かります。
retireアクションによるトークン回収(還収)
続いて、トークン発行の逆操作である、トークンの回収を実行してみましょう。これは、トークンの流通量を減少させる操作で、経済用語では還収とも呼ばれます。
コード3に、トークンの回収をおこなうretireアクションの抜粋を示します。
コード3. eosio.token.cppのretireアクション実装抜粋
void token::retire( asset quantity, string memo )
{
auto sym = quantity.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" );
stats statstable( _self, sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
eosio_assert( existing != statstable.end(), "token with symbol does not exist" );
const auto& st = *existing;
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must retire positive quantity" );
eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" );
statstable.modify( st, same_payer, [&]( auto& s ) {
s.supply -= quantity;
});
sub_balance( st.issuer, quantity );
}
トークンの回収をおこなえるのはトークンの発行者であるissuerです。ただし、aliceやbobの保有しているトークンを勝手には回収できないため、まずはissuerが流通しているトークンの一部を保有する必要があります。
コマンド7を用いて、まずはaliceが保有している 75.0000 SYS をissuerであるeosioアカウントに送付します。
コマンド7. aliceからeosioに75.0000 SYSを送付する
$ cleos push action eosio.token transfer '[ "alice", "eosio", "75.0000 SYS", "m" ]' -p alice@active
$ cleos get table eosio.token eosio accounts
{
"rows": [{
"balance": "75.0000 SYS"
}
],
"more": false
}
eosioの残高を確認すると、正しく 75.0000 SYS が送られていることが分かります。
続いて、コマンド8を用いて、eosioアカウントの残高から10.0000 SYSを回収するretireアクションを実行します。
コマンド8. eosioアカウントの残高から10.0000 SYSを回収する
$ cleos push action eosio.token retire '[ "10.0000 SYS", "memo" ]' -p eosio@active
executed transaction: 175e41ebaef1edb4fcfccded22da15c34e15ccfcfd9a585769a38ce970d0265a 120 bytes 1365 us
# eosio.token <= eosio.token::retire {"quantity":"10.0000 SYS","memo":"memo"}
$ cleos get table eosio.token eosio accounts
{
"rows": [{
"balance": "65.0000 SYS"
}
],
"more": false
}
上記コマンドから、eosioの残高が 65.0000 SYS に減少していることが分かります。
コマンド9 を用いて、SYSトークンの流通量も確認しておきましょう。
コマンド9. SYSトークンのstatを確認する
$ cleos get table eosio.token SYS stat
{
"rows": [{
"supply": "90.0000 SYS",
"max_supply": "1000000000.0000 SYS",
"issuer": "eosio"
}
],
"more": false
}
SYSトークンの流通量が100.0000 SYSから90.0000 SYSに減少し、10.0000 SYSが市場から回収されていることが分かります。
open/closeアクションによる残高口座の開設/閉鎖
最後に、open/closeアクションの動作確認をしてみましょう。
まず、closeアクションの実装をコード4に示します。
コード4. eosio.token.cppのcloseアクション実装抜粋
void token::close( name owner, const symbol& symbol )
{
require_auth( owner );
accounts acnts( _self, owner.value );
auto it = acnts.find( symbol.code().raw() );
eosio_assert( it != acnts.end(), "Balance row already deleted or never existed. Action won't have any effect." );
eosio_assert( it->balance.amount == 0, "Cannot close because the balance is not zero." );
acnts.erase( it );
}
closeアクションは、アカウントに紐付く特定のトークン残高のレコードを削除するアクションです。残高のレコードを削除するには、あらかじめ残トークンの残高が0になっている必要があります。
例えば、現在のaliceのSYSトークンの残高は0のはずです。下記コマンド10で改めて確認してみましょう。
コマンド10. aliceの残高を確認する
$ cleos get table eosio.token alice accounts
{
"rows": [{
"balance": "0.0000 SYS"
}
],
"more": false
}
続いて、下記コマンド11を用いて、aliceのSYSトークンの残高レコードを削除してみます。これまでのアクションとトークンを指定する引数の型が異なっているので注意してください。
コマンド11. aliceのSYSトークンの残高レコードを削除する
$ cleos push action eosio.token close '[ "alice", "4,SYS" ]' -p alice@active
executed transaction: 7c68ea491ebc9f9ab64dd755b127a8e838d44090ad92e54c691768302d3372cf 112 bytes 288 us
# eosio.token <= eosio.token::close {"owner":"alice","symbol":"4,SYS"}
$ cleos get table eosio.token alice accounts
{
"rows": [],
"more": false
}
closeアクションにより、aliceのSYSトークンの残高を示すレコードが削除されていることが分かります。
closeの逆操作をするアクションがopenアクションです。コード5に、openアクションの実装抜粋を示します。
openアクションにより、指定したトークンの残高を保持するレコードが追加されます。
コード5. eosio.token.cppのopenアクション実装抜粋
void token::open( name owner, const symbol& symbol, name ram_payer )
{
require_auth( ram_payer );
auto sym_code_raw = symbol.code().raw();
stats statstable( _self, sym_code_raw );
const auto& st = statstable.get( sym_code_raw, "symbol does not exist" );
eosio_assert( st.supply.symbol == symbol, "symbol precision mismatch" );
accounts acnts( _self, owner.value );
auto it = acnts.find( sym_code_raw );
if( it == acnts.end() ) {
acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = asset{0, symbol};
});
}
}
aliceのアカウントにSYSトークンの残高を保持するレコードを追加するopenアクションの実行例をコマンド12に示します。
コマンド12. aliceにSYSトークンの残高口座を開設する
$ cleos push action eosio.token open '[ "alice", "4,SYS", "alice" ]' -p alice@active
executed transaction: 9115fd79e90cefd2509b701e68a194d57eece59f325ba45e261090f23ebd748b 120 bytes 396 us
# eosio.token <= eosio.token::open {"owner":"alice","symbol":"4,SYS","ram_payer":"alice"}
$ cleos get table eosio.token alice accounts
{
"rows": [{
"balance": "0.0000 SYS"
}
],
"more": false
}
これにより、0.0000 SYSで初期化されたトークン残高がaliceアカウントに開設されました。
本稿までで、eosio.tokenコントラクトを用いたトークンの基本的な操作方法を紹介しました。紹介したコマンドを応用して、独自のトークン作成やさまざまなアクションを試し、EOSのコントラクト操作について慣れてみてください。
参考文献:
- EOSIO Developer Portal – 2.2 Deploy, Issue and Transfer Tokens
https://developers.eos.io/eosio-home/docs/token-contract - B9lab Academy – Introduction to EOSIO for Developers
https://academy.b9lab.com/courses/course-v1:B9lab+EOSIO-FREE+2018-09/about - Understanding The eosio.token Contract
https://medium.com/coinmonks/understanding-the-eosio-token-contract-87466b9fdca9