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 
");    
 10     eosio_assert(pubkey.size() > 0, "pubkey empty 
");
 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
");
 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 
");
 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 
");
 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 
");
 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.
");
 81         
 82         auto existing_owner = ass.find(_self);
 83         eosio_assert(existing_owner != ass.end(), " owner user not fund.
");
 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 
");
112     eosio_assert( to > 0, "to empty 
");
113     eosio_assert(funds > 0, "funds error 
");
114     
115     require_auth(from);
116 
117     eosio_assert(is_exist_account(to), "fund receiver user failed on userinfo table. 
");
118     
119     asset ass(_self, _self);
120 
121     auto existing_from = ass.find(from);
122     eosio_assert(existing_from != ass.end(), "fund sender failed.
");
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
");
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
");
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. 
");
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. 
");    
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. 
");
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,欢迎大家一起交流学习!

原文地址:https://www.cnblogs.com/hbright/p/9408048.html