EOS 智能合约编写(一)
本文编写了一个简单的EOS智能合约,实现用户管理和资产管理,包括存钱,取钱,转帐的功能,旨在学习如何编写自己的EOS合约功能。
系统:Ubuntu EOS版本:v1.1.1
一.智能合约代码
1 #ifndef __AWARD_H__ 2 #define __AWARD_H__ 3 4 #include <eosiolib/eosio.hpp> 5 6 namespace eosio { 7 8 9 10 class keephand : public eosio:: contract { 11 12 private: 13 /// @abi table userinfo i64 14 struct st_userinfo { 15 account_name name; 16 std::string pubkey; 17 18 uint64_t get_pubkey() const { return string_to_name(pubkey.data()); } 19 uint64_t primary_key() const { return name; } 20 21 EOSLIB_SERIALIZE(st_userinfo, (name)(pubkey)) 22 }; 23 24 // @abi table 25 typedef eosio::multi_index<N(userinfo), st_userinfo, indexed_by<N(pubkey), const_mem_fun<st_userinfo, uint64_t, &st_userinfo::get_pubkey>> > user; 26 27 // @abi table userasset i64 28 struct st_asset { 29 account_name name; 30 double funds; 31 32 uint64_t primary_key() const { return name; } 33 //uint64_t get_funds() const { return funds; } 34 35 EOSLIB_SERIALIZE(st_asset, (name)(funds)) 36 }; 37 38 // @abi table 39 typedef eosio::multi_index<N(userasset), st_asset/*, indexed_by<N(funds), const_mem_fun<st_asset, uint64_t, &st_asset::get_funds>>*/ > asset; 40 private: 41 static constexpr double fee_rate = 0.01000000; // rate 42 static constexpr double fee_min = 0.01000000; // the smallest funds of take money once time 43 44 enum en_fee_type { 45 EN_TAKE = 1, // take money 46 EN_TRANSFER, 47 }; 48 49 private: 50 void create_asset_data_by_account(const account_name name); 51 void delete_asset_data_by_account(const account_name name); 52 53 bool is_exist_account(const account_name name); 54 void funds_option(const account_name& name, const double& funds, bool opt = false); // option: true -- add false--sub 55 void modify_asset(const account_name& name, const double& funds) const; 56 57 public: 58 keephand(account_name self):contract(self) {} 59 60 // @abi action 61 void createacnt( const account_name& name, const std::string& pubkey); 62 63 // @abi action 64 void deleteacnt(const account_name& name); 65 66 // @abi action 67 void modasset(const account_name& name, const double& funds, bool bsaving = false ); 68 69 // @abi action 70 void transfer(const account_name& from, const account_name& to, const double& funds, const std::string& str ); 71 72 }; 73 74 } 75 76 EOSIO_ABI(eosio::keephand, (createacnt)(deleteacnt)(modasset) (transfer) ) 77 78 #endif
1 #include <eosiolib/eosio.hpp> 2 #include <eosiolib/print.hpp> 3 #include "./keephand.hpp" 4 5 using namespace eosio; 6 7 void keephand::createacnt( const account_name& name, const std::string& pubkey) { 8 9 eosio_assert(name > 0, "name empty \n"); 10 eosio_assert(pubkey.size() > 0, "pubkey empty \n"); 11 12 //check auth 13 require_auth(_self); 14 user actns(_self, _self); 15 auto exist = actns.find(name); 16 eosio_assert( exist == actns.end(), "name already exists\n"); 17 18 //modify data 19 actns.emplace( _self, [&]( auto& n) { 20 n.name = name; 21 n.pubkey = pubkey; 22 }); 23 24 create_asset_data_by_account(name); 25 } 26 27 void keephand::deleteacnt(const account_name& name) { 28 29 eosio_assert(name > 0, "name empty \n"); 30 31 require_auth( _self ); 32 user acnts( _self, _self); 33 34 auto existing = acnts.find(name); 35 eosio_assert(existing != acnts.end(), "name not found \n"); 36 37 acnts.erase(existing); 38 delete_asset_data_by_account(name); 39 } 40 41 void keephand:: create_asset_data_by_account(const account_name name) { 42 43 asset ass(_self, _self); 44 ass.emplace(_self, [&](auto& d) { 45 d.name = name; 46 d.funds = 0; 47 }); 48 49 } 50 51 void keephand::modasset(const account_name& name, const double& funds, bool bsaving ) { 52 53 eosio_assert( name > 0, "name empty \n"); 54 eosio_assert( funds > 0, "funds input error"); 55 56 user acnts(_self, _self); 57 58 auto existing_user = acnts.find(name); 59 60 eosio_assert(existing_user != acnts.end(), "name not foud on userinfo"); 61 62 //check auth 63 require_auth(name); 64 65 asset ass(_self, _self); 66 auto existing_asset = ass.find(name); 67 //print("keephand::modasset() find name in asset: ", (existing_asset == ass.end() ? "false" : "true")); 68 eosio_assert(existing_asset != ass.end(), "name not fund on asset" ); 69 70 71 72 if(existing_asset != ass.end()) { 73 74 double op_funds = 0.00000000; 75 if(bsaving) { 76 op_funds = existing_asset->funds + funds; 77 } else { 78 //is enough funds 79 op_funds = existing_asset->funds - funds - funds * fee_rate; 80 eosio_assert(op_funds >= 0, "not enough asset.\n"); 81 82 auto existing_owner = ass.find(_self); 83 eosio_assert(existing_owner != ass.end(), " owner user not fund.\n"); 84 85 ass.modify(*existing_owner, _self , [&]( auto& d ) { 86 d.name = _self; 87 d.funds = funds* fee_rate; 88 }); 89 } 90 91 92 93 ass.modify(*existing_asset, _self , [&]( auto& d ) { 94 d.name = name; 95 d.funds = op_funds; 96 }); 97 } 98 } 99 void keephand::delete_asset_data_by_account(const account_name name) 100 { 101 asset ass(_self, _self); 102 auto existing = ass.find(name); 103 104 if(existing != ass.end()) { 105 ass.erase(existing); 106 //print("delete asset name=", name); 107 } 108 } 109 110 void keephand::transfer(const account_name& from, const account_name& to, const double& funds, const std::string& strremark ) { 111 eosio_assert( from > 0, "from empty \n"); 112 eosio_assert( to > 0, "to empty \n"); 113 eosio_assert(funds > 0, "funds error \n"); 114 115 require_auth(from); 116 117 eosio_assert(is_exist_account(to), "fund receiver user failed on userinfo table. \n"); 118 119 asset ass(_self, _self); 120 121 auto existing_from = ass.find(from); 122 eosio_assert(existing_from != ass.end(), "fund sender failed.\n"); 123 124 double fee = funds * fee_rate; 125 if(fee < fee_min) { 126 fee = fee_min; 127 } 128 double asset_sender = existing_from->funds - fee - funds; 129 eosio_assert(asset_sender >= 0.00000000, " sender not enough asset."); 130 131 funds_option(from, fee + funds, false); 132 133 auto existing_to = ass.find(to); 134 eosio_assert(existing_to != ass.end(), "fund receiver failed on asset table\n"); 135 funds_option(to, funds, true ); 136 137 auto existing_self = ass.find(_self); 138 eosio_assert(existing_self != ass.end(), "fund self failed on asset table\n"); 139 funds_option(_self, fee, true ); 140 } 141 142 bool keephand::is_exist_account(const account_name name) { 143 user acnts(_self, _self); 144 auto idx = acnts.find(name); 145 146 return idx != acnts.end(); 147 } 148 149 150 void keephand::funds_option(const account_name& name, const double& funds, bool opt) { 151 asset ass(_self, _self); 152 auto idx = ass.find(name); 153 eosio_assert(idx != ass.end(), "fund name failed. \n"); 154 155 double user_asset = 0.00000000; 156 if(opt) { 157 user_asset = idx->funds + funds; 158 } else { 159 user_asset = idx->funds - funds; 160 } 161 162 eosio_assert(user_asset >= 0, "asset not enought. \n"); 163 164 modify_asset(name, user_asset); 165 } 166 167 void keephand::modify_asset(const account_name& name, const double& funds) const { 168 asset ass(_self, _self); 169 auto idx = ass.find(name); 170 eosio_assert(idx != ass.end(), "fund name failed on asset table. \n"); 171 172 ass.modify(*idx, _self , [&]( auto& a ) { 173 a.name = name; 174 a.funds = funds; 175 }); 176 }
二.测试流程:
1.创建用户 eoskeephand2 与 eoseosbright //owner 5JYQB6xwraFX53yaMnhht5bnceZRdS8fFQR2jxdeAFcj5DVjHK9 EOS7er3MePm841zP3VKzCvUhcbqwEy9BRXthZCBZu7ihPfvP4Rtg4 //active 5Jw9zKATACsqtTHJam4ciVYWoh9EBny93L1nswyazQjnEoUa9by EOS723pRnWd43Lu53LWUq7ciVrmUXvADUoVsjo3ZhDHBi8jDWduYD 创建钱包 cleos --wallet-url http://127.0.0.1:8901 wallet create -n walletkeephand2 PW5JqBoV8GQhVP9GPrJwN7N4oGQ9yrm53LdjST6gYD862eeKM5aWb //导入钱包 cleos --wallet-url http://127.0.0.1:8901 wallet import -n walletkeephand2 --private-key 5JYQB6xwraFX53yaMnhht5bnceZRdS8fFQR2jxdeAFcj5DVjHK9 cleos --wallet-url http://127.0.0.1:8901 wallet import -n walletkeephand2 --private-key 5Jw9zKATACsqtTHJam4ciVYWoh9EBny93L1nswyazQjnEoUa9by //创建eoskeephand2用户 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.141:8888 system newaccount --transfer hml eoskeephand2 EOS7er3MePm841zP3VKzCvUhcbqwEy9BRXthZCBZu7ihPfvP4Rtg4 EOS723pRnWd43Lu53LWUq7ciVrmUXvADUoVsjo3ZhDHBi8jDWduYD --stake-net "200.0000 SYS" --stake-cpu "200.0000 SYS" --buy-ram "200.0000 SYS" cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.141:8888 transfer hml eoskeephand2 "200.0000 SYS" cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.141:8888 system newaccount --transfer hml eoseosbright EOS7er3MePm841zP3VKzCvUhcbqwEy9BRXthZCBZu7ihPfvP4Rtg4 EOS723pRnWd43Lu53LWUq7ciVrmUXvADUoVsjo3ZhDHBi8jDWduYD --stake-net "200.0000 SYS" --stake-cpu "200.0000 SYS" --buy-ram "200.0000 SYS" cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.141:8888 transfer hml eoseosbright "200.0000 SYS" 重复上面步骤自己创建一个用户 eoseosbright 2.编译与加载合约 eosiocpp -o keephand.wast keephand.cpp eosiocpp -g keephand.abi keephand.cpp //加载合约 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 set contract eoskeephand2 ~/eos/contracts/keephand/ -p eoskeephand2 //查询数据库 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 get table eoskeephand2 eoskeephand2 userinfo cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 get table eoskeephand2 eoskeephand2 userasset 3.增加合约创建者用户 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"eoskeephand2","pubkey":"EOS723pRnWd43Lu53LWUq7ciVrmUXvADUoVsjo3ZhDHBi8jDWduYD"}' -p eoskeephand2 以后的手率费都会放在这个用户下面 4.创建其它的一些用户usera,userb,userc cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"eoseosbright","pubkey":"EOS8KjRh1QFLqNECdqg7QXiBnv3F2DhVSQDdSkeFJX2ZLkR13p8Cs"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"eosxiaomingg","pubkey":"EOS8YHwHXqEdBcNLdNud4Uuste5kbsPyHwSzMuUWCygVHYNr7Gk7r"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"usera","pubkey":"EOS8Hn8Bbp5oska1LULgHFr2JPP2pGZmkktdYF5e1c1HBPVBakrBY"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"userb","pubkey":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 createacnt '{"name":"userc","pubkey":"EOS6XpxPXQz9zxpRoZwcX7qxZBGM5AW9UXmXx5gPj8TQsce2djSkn"}' -p eoskeephand2 5.清除用户数据 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"eoseosbright","pubkey":"EOS8KjRh1QFLqNECdqg7QXiBnv3F2DhVSQDdSkeFJX2ZLkR13p8Cs"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"eosxiaomingg","pubkey":"EOS8YHwHXqEdBcNLdNud4Uuste5kbsPyHwSzMuUWCygVHYNr7Gk7r"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"usera","pubkey":"EOS8Hn8Bbp5oska1LULgHFr2JPP2pGZmkktdYF5e1c1HBPVBakrBY"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"userb","pubkey":"EOS7Ef4kuyTbXbtSPP5Bgethvo6pbitpuEz2RMWhXb8LXxEgcR7MC"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"userc","pubkey":"EOS6XpxPXQz9zxpRoZwcX7qxZBGM5AW9UXmXx5gPj8TQsce2djSkn"}' -p eoskeephand2 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 deleteacnt '{"name":"eoskeephand2","pubkey":"EOS723pRnWd43Lu53LWUq7ciVrmUXvADUoVsjo3ZhDHBi8jDWduYD"}' -p eoskeephand2 只有合约所有者才有权限清除 6.存款取款 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 modasset '{"name":"eoseosbright","funds":"1000", "bsaving":"1"}' -p eoseosbright cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 modasset '{"name":"eosxiaomingg","funds":"100", "bsaving":"0"}' -p eosxiaomingg 7.转帐 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 transfer '{"from":"eosxiaomingg","to":"eoseosbright","funds":"100.0000","str":"thanks"}' -p eosxiaomingg 8.单用户查询余额 cleos --wallet-url http://127.0.0.1:8901 --url http://10.186.11.223:8888 push action eoskeephand2 queryasset '{"name":"eosxiaomingg"}' -p eosxiaomingg
三.测试功能
运行到测试步骤的第三步后查看用户信息与资产是这样的
运行第四步再查看
现在我们有六个用户了,对应的初始资产为0,下面为存钱的测试
看到eoseosbright的资产增加了,再做取钱的测试:
eoseosbright的资产减少1010,是因为取钱时收取了1%的手率费,手率费存到合约用户创建者用户eoskeephand2上了,再来做做转帐的测试。
转帐也是收了1%的手率费用的,验证OK。
四.智能合约编写注意事项
1.能够让用户使用的函数名称有限制,只能使用数据加字母,不能加下划线,如下所示:
EOSIO_ABI(eosio::keephand, (createacnt)(deleteacnt)(modasset) (transfer) )
2.代码中类似带@的注释是必须的,否则虚拟机无法编译通过,所以不要漏写或者改写及删除
// @abi table userinfo i64
// @abi table
// @abi action
3.智能合约中多索引数据库使用的索引是uint64_t类型,所以如果你使用其它类型当一级索引或者二级索引不匹配的话会无法通过编译,必须自己进行唯一类型转化(保证键的唯一性),不过这里应该可以进行非唯一索引,暂时没有探究;
有问题请联系QQ:289093099,欢迎大家一起交流学习!