一 运行eos客户端,启动私有网络
./nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::account_history_api_plugin
eosio generated block bcc817bc... #637 @ 2018-05-10T09:22:13.000 with 0 trxs, lib: 636
eosio generated block 527d07ae... #638 @ 2018-05-10T09:22:13.500 with 0 trxs, lib: 637
二 创建钱包
./cleos wallet create
会返回一个钱包私钥
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5J9ASbLXgn2RmuW2YdHwuzsys3YJkGJonegRmxK52DyWCKcYNXn"
(1)钱包私钥可以用于钱包解锁,
命令为 “cleos wallet unlock --password 私钥”
(2)如重复执行“./cleos wallet create”,则报错,提示钱包文件已存在
Error 3140001: Wallet already exists
Try to use different wallet name.
Error Details:
Wallet with name: 'default' already exists at /home/li/.local/share/eosio/nodeos/data/./default.wallet
三 加载 bios 合约
./cleos set contract eosio ../../contracts/eosio.bios -p eosio
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: 24cee6eb3def42662b3cc2879e75f6b3386c8d5b8dda2eb31d1bffd5170d0c80 3280 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"eosio","vmtype":0,"vmversion":0,"code":"0061736d0100000001581060037f7e7f0060057f7e7e7e7e...
# eosio <= eosio::setabi {"account":"eosio","abi":{"types":[],"structs":[{"name":"set_account_limits","base":"","fields":[{"n...
注意:eosio.bios 路径根据实际情况选择目录
如果我们把 EOS 看做一个操作系统,那么 bios 合约就是让我们能进行最底层的操作的合约,其它合约的运行建立在 bios 合约的基础之上,所以我们先来加载 bios 合约。
四 创建账户
(1)生成密钥对
./cleos create key
Private key: 5Ke4aKjY9PGZJnxNV54REsGUqQqRZNpJKr6unnkWLmU1oTT16Cw
Public key: EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm
(2)把用户私钥导入钱包
./cleos wallet import 5Ke4aKjY9PGZJnxNV54REsGUqQqRZNpJKr6unnkWLmU1oTT16Cw
imported private key for: EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm
(3)用公钥创建账户
./cleos create account eosio bytemaster EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm
executed transaction: 78d3a0752630842bc08d99a2d584982b8ccc89a98b0b5aeadd0792ea76737830 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"bytemaster","owner":{"threshold":1,"keys":[{"key":"EOS7KYYgetJuZ7SpkQj9aD...
(4)查看账户,确实创建成功了
./cleos get account bytemaster
{
"account_name": "bytemaster",
"permissions": [{
"perm_name": "active",
"parent": "owner",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm",
"weight": 1
}
],
"accounts": []
}
},{
"perm_name": "owner",
"parent": "",
"required_auth": {
"threshold": 1,
"keys": [{
"key": "EOS7KYYgetJuZ7SpkQj9aDw2Sui7EuLGsKqRp7gq7qQZMx7gkfcGm",
"weight": 1
}
],
"accounts": []
}
}
]
}
(5)另外再创建一个账户,tester
./cleos create key
Private key: 5KTVgaQwPkyCxJdZqTiZLMbxjZfcqKqFLnLSoUvuGUoUdvTCFaR
Public key: EOS6H3yfhgcrH1mJs92bbvzYAakMShjcj8SmnmQd3NFBSwVz6EKyM
./cleos wallet import 5KTVgaQwPkyCxJdZqTiZLMbxjZfcqKqFLnLSoUvuGUoUdvTCFaR
imported private key for: EOS6H3yfhgcrH1mJs92bbvzYAakMShjcj8SmnmQd3NFBSwVz6EKyM
./cleos create account eosio tester
EOS6H3yfhgcrH1mJs92bbvzYAakMShjcj8SmnmQd3NFBSwVz6EKyM EOS6H3yfhgcrH1mJs92bbvzYAakMShjcj8SmnmQd3NFBSwVz6EKyM
executed transaction: 8ff09b833b2c9a8b9143af165158e052c5ae10c8f35554aa678a95c962d1fd58 352 bytes 102400 cycles
# eosio <= eosio::newaccount {"creator":"eosio","name":"tester","owner":{"threshold":1,"keys":[{"key":"EOS6H3yfhgcrH1mJs92bbvzYAa...
五 部署货币合约
(1)查看bytemaster的合约
./cleos get code bytemaster
code hash: 0000000000000000000000000000000000000000000000000000000000000000
(2)部署代币合约
./cleos set contract bytemaster ../../contracts/eosio.token
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: e1b0fdb6626b4e64640efa8a63cf6c6fb487c5b9897c30ba9778667827b91425 8040 bytes 2200576 cycles
# eosio <= eosio::setcode {"account":"bytemaster","vmtype":0,"vmversion":0,"code":"0061736d01000000018a011660067f7e7f7f7f7f006...
# eosio <= eosio::setabi {"account":"bytemaster","abi":{"types":[],"structs":[{"name":"transfer","base":"","fields":[{"name":...
(3)查看bytemaster的合约,看到代币合约已经部署
./cleos get code bytemaster
code hash: 7c6a300874835ad928de4f30712023758157bd50cb423ab039443f56a84167ff
(4)创建代币
./cleos push action bytemaster create '[ "bytemaster", "1000000000.0000 EOS", 0, 0, 0]' -p bytemaster@active
executed transaction: 56f722fc175854b52a56d32c98d41d3e1c8b3bcfb9cc8c2520503b04bc037a68 248 bytes 104448 cycles
# bytemaster <= bytemaster::create {"issuer":"bytemaster","maximum_supply":"1000000000.0000 EOS","can_freeze":0,"can_recall":0,"can_whi...
其中create接口,在eosio.token.abi有声明
六 发行代币
./cleos push action bytemaster issue '[ "bytemaster", "100.0000 EOS", "memo" ]' -p bytemaster
issue '[ "bytemaster", "100.0000 EOS", "memo" ]' -p bytemaster
executed transaction: 4f932f48780e3c83d82ba263844302398f42f79e8bd2b52d3f8eaa2bc0deb79e 256 bytes 107520 cycles
# bytemaster <= bytemaster::issue {"to":"bytemaster","quantity":"100.0000 EOS","memo":"memo"}
>> issue
其中issue接口,在eosio.token.abi有声明
七 测试代币转账
./cleos push action bytemaster transfer '[ "bytemaster", "tester", "25.0000 EOS", "m" ]' -p bytemaster
executed transaction: e7ebf695a213842dc3049f388fda128a8d9c05d078273bc75eeb5863fabcd104 256 bytes 109568 cycles
# bytemaster <= bytemaster::transfer {"from":"bytemaster","to":"tester","quantity":"25.0000 EOS","memo":"m"}
>> transfer
# tester <= bytemaster::transfer {"from":"bytemaster","to":"tester","quantity":"25.0000 EOS","memo":"m"}
其中transfer接口,在eosio.token.abi有声明
八 总结
《1 了解发布token的顺序
启动私有链
创建钱包
加载 bios 合约
创建用户
部署token合约
创建代币
发行代币
九 引用
https://github.com/EOSIO/eos/wiki/Tutorial-eosio-token-Contract 《Tutorial eosio token Contract》
http://liyuechun.org/2018/04/13/eos-contract/ 《第二篇 - 从零到壹学习EOS - EOS Currency 合约案例》
https://mp.weixin.qq.com/s/3_4KCu8YcbGlEO2Pn6vK_A 《最友善EOS开发教程——从如何发行「新垣结衣币」说起》
十 eosio.token合约相关文件内容
包含(eosio.token.abi, eosio.token.hpp eosio.token.cpp)
eosio.token.abi
{
"types": [],
"structs": [{
"name": "transfer",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"},
{"name":"memo", "type":"string"}
]
},{
"name": "create",
"base": "",
"fields": [
{"name":"issuer", "type":"account_name"},
{"name":"maximum_supply", "type":"asset"},
{"name":"can_freeze", "type":"uint8"},
{"name":"can_recall", "type":"uint8"},
{"name":"can_whitelist", "type":"uint8"}
]
},{
"name": "issue",
"base": "",
"fields": [
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"},
{"name":"memo", "type":"string"}
]
},{
"name": "account",
"base": "",
"fields": [
{"name":"balance", "type":"asset"},
{"name":"frozen", "type":"uint8"},
{"name":"whitelist", "type":"uint8"}
]
},{
"name": "currency_stats",
"base": "",
"fields": [
{"name":"supply", "type":"asset"},
{"name":"max_supply", "type":"asset"},
{"name":"issuer", "type":"account_name"},
{"name":"can_freeze", "type":"uint8"},
{"name":"can_recall", "type":"uint8"},
{"name":"can_whitelist", "type":"uint8"},
{"name":"is_frozen", "type":"uint8"},
{"name":"enforce_whitelist", "type":"uint8"}
]
}
],
"actions": [{
"name": "transfer",
"type": "transfer",
"ricardian_contract": ""
},{
"name": "issue",
"type": "issue",
"ricardian_contract": ""
}, {
"name": "create",
"type": "create",
"ricardian_contract": ""
}
],
"tables": [{
"name": "accounts",
"type": "account",
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
},{
"name": "stat",
"type": "currency_stats",
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
}
],
"ricardian_clauses": []
}
eosio.token.hpp
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include
#include
#include
namespace eosiosystem {
class system_contract;
}
namespace eosio {
using std::string;
class token : public contract {
public:
token( account_name self ):contract(self){}
void create( account_name issuer,
asset maximum_supply,
uint8_t issuer_can_freeze,
uint8_t issuer_can_recall,
uint8_t issuer_can_whitelist );
void issue( account_name to, asset quantity, string memo );
void transfer( account_name from,
account_name to,
asset quantity,
string memo );
private:
friend eosiosystem::system_contract;
inline asset get_supply( symbol_name sym )const;
inline asset get_balance( account_name owner, symbol_name sym )const;
private:
struct account {
asset balance;
bool frozen = false;
bool whitelist = true;
uint64_t primary_key()const { return balance.symbol.name(); }
};
struct currency_stats {
asset supply;
asset max_supply;
account_name issuer;
bool can_freeze = true;
bool can_recall = true;
bool can_whitelist = true;
bool is_frozen = false;
bool enforce_whitelist = false;
uint64_t primary_key()const { return supply.symbol.name(); }
};
typedef eosio::multi_index accounts;
typedef eosio::multi_index stats;
void sub_balance( account_name owner, asset value, const currency_stats& st );
void add_balance( account_name owner, asset value, const currency_stats& st,
account_name ram_payer );
public:
struct transfer_args {
account_name from;
account_name to;
asset quantity;
string memo;
};
};
asset token::get_supply( symbol_name sym )const
{
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
return st.supply;
}
asset token::get_balance( account_name owner, symbol_name sym )const
{
accounts accountstable( _self, owner );
const auto& ac = accountstable.get( sym );
return ac.balance;
}
} /// namespace eosio
eosio.token.cpp
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include
namespace eosio {
void token::create( account_name issuer,
asset maximum_supply,
uint8_t issuer_can_freeze,
uint8_t issuer_can_recall,
uint8_t issuer_can_whitelist )
{
require_auth( _self );
auto sym = maximum_supply.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" );
eosio_assert( maximum_supply.is_valid(), "invalid supply");
eosio_assert( maximum_supply.amount > 0, "max-supply must be positive");
stats statstable( _self, sym.name() );
auto existing = statstable.find( sym.name() );
eosio_assert( existing == statstable.end(), "token with symbol already exists" );
statstable.emplace( _self, [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
s.can_freeze = issuer_can_freeze;
s.can_recall = issuer_can_recall;
s.can_whitelist = issuer_can_whitelist;
});
}
void token::issue( account_name to, asset quantity, string memo )
{
print( "issue" );
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
require_auth( st.issuer );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must issue positive quantity" );
eosio_assert( quantity <= st.max_supply - st.supply, "quantity exceeds available supply");
statstable.modify( st, 0, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st, st.issuer );
if( to != st.issuer )
{
SEND_INLINE_ACTION( *this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo} );
}
}
void token::transfer( account_name from,
account_name to,
asset quantity,
string /*memo*/ )
{
print( "transfer" );
require_auth( from );
auto sym = quantity.symbol.name();
stats statstable( _self, sym );
const auto& st = statstable.get( sym );
require_recipient( from );
require_recipient( to );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must transfer positive quantity" );
sub_balance( from, quantity, st );
add_balance( to, quantity, st, from );
}
void token::sub_balance( account_name owner, asset value, const currency_stats& st ) {
accounts from_acnts( _self, owner );
const auto& from = from_acnts.get( value.symbol.name() );
eosio_assert( from.balance.amount >= value.amount, "overdrawn balance" );
if( has_auth( owner ) ) {
eosio_assert( !st.can_freeze || !from.frozen, "account is frozen by issuer" );
eosio_assert( !st.can_freeze || !st.is_frozen, "all transfers are frozen by issuer" );
eosio_assert( !st.enforce_whitelist || from.whitelist, "account is not white listed" );
} else if( has_auth( st.issuer ) ) {
eosio_assert( st.can_recall, "issuer may not recall token" );
} else {
eosio_assert( false, "insufficient authority" );
}
from_acnts.modify( from, owner, [&]( auto& a ) {
a.balance -= value;
});
}
void token::add_balance( account_name owner, asset value, const currency_stats& st, account_name ram_payer )
{
accounts to_acnts( _self, owner );
auto to = to_acnts.find( value.symbol.name() );
if( to == to_acnts.end() ) {
eosio_assert( !st.enforce_whitelist, "can only transfer to white listed accounts" );
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
eosio_assert( !st.enforce_whitelist || to->whitelist, "receiver requires whitelist by issuer" );
to_acnts.modify( to, 0, [&]( auto& a ) {
a.balance += value;
});
}
}
} /// namespace eosio
EOSIO_ABI( eosio::token, (create)(issue)(transfer) )