本文编写了一个简单的EOS智能合约,实现用户管理和资产管理,包括存钱,取钱,转帐的功能,旨在学习如何编写自己的EOS合约功能。

系统:Ubuntu      EOS版本:v1.1.1

  

一.智能合约代码

 #ifndef __AWARD_H__
#define __AWARD_H__ #include <eosiolib/eosio.hpp> namespace eosio { class keephand : public eosio:: contract { private:
/// @abi table userinfo i64
struct st_userinfo {
account_name name;
std::string pubkey; uint64_t get_pubkey() const { return string_to_name(pubkey.data()); }
uint64_t primary_key() const { return name; } EOSLIB_SERIALIZE(st_userinfo, (name)(pubkey))
}; // @abi table
typedef eosio::multi_index<N(userinfo), st_userinfo, indexed_by<N(pubkey), const_mem_fun<st_userinfo, uint64_t, &st_userinfo::get_pubkey>> > user; // @abi table userasset i64
struct st_asset {
account_name name;
double funds; uint64_t primary_key() const { return name; }
//uint64_t get_funds() const { return funds; } EOSLIB_SERIALIZE(st_asset, (name)(funds))
}; // @abi table
typedef eosio::multi_index<N(userasset), st_asset/*, indexed_by<N(funds), const_mem_fun<st_asset, uint64_t, &st_asset::get_funds>>*/ > asset;
private:
static constexpr double fee_rate = 0.01000000; // rate
static constexpr double fee_min = 0.01000000; // the smallest funds of take money once time enum en_fee_type {
EN_TAKE = , // take money
EN_TRANSFER,
}; private:
void create_asset_data_by_account(const account_name name);
void delete_asset_data_by_account(const account_name name); bool is_exist_account(const account_name name);
void funds_option(const account_name& name, const double& funds, bool opt = false); // option: true -- add false--sub
void modify_asset(const account_name& name, const double& funds) const; public:
keephand(account_name self):contract(self) {} // @abi action
void createacnt( const account_name& name, const std::string& pubkey); // @abi action
void deleteacnt(const account_name& name); // @abi action
void modasset(const account_name& name, const double& funds, bool bsaving = false ); // @abi action
void transfer(const account_name& from, const account_name& to, const double& funds, const std::string& str ); }; } EOSIO_ABI(eosio::keephand, (createacnt)(deleteacnt)(modasset) (transfer) ) #endif
 #include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include "./keephand.hpp" using namespace eosio; void keephand::createacnt( const account_name& name, const std::string& pubkey) { eosio_assert(name > , "name empty \n");
eosio_assert(pubkey.size() > , "pubkey empty \n"); //check auth
require_auth(_self);
user actns(_self, _self);
auto exist = actns.find(name);
eosio_assert( exist == actns.end(), "name already exists\n"); //modify data
actns.emplace( _self, [&]( auto& n) {
n.name = name;
n.pubkey = pubkey;
}); create_asset_data_by_account(name);
} void keephand::deleteacnt(const account_name& name) { eosio_assert(name > , "name empty \n"); require_auth( _self );
user acnts( _self, _self); auto existing = acnts.find(name);
eosio_assert(existing != acnts.end(), "name not found \n"); acnts.erase(existing);
delete_asset_data_by_account(name);
} void keephand:: create_asset_data_by_account(const account_name name) { asset ass(_self, _self);
ass.emplace(_self, [&](auto& d) {
d.name = name;
d.funds = ;
}); } void keephand::modasset(const account_name& name, const double& funds, bool bsaving ) { eosio_assert( name > , "name empty \n");
eosio_assert( funds > , "funds input error"); user acnts(_self, _self); auto existing_user = acnts.find(name); eosio_assert(existing_user != acnts.end(), "name not foud on userinfo"); //check auth
require_auth(name); asset ass(_self, _self);
auto existing_asset = ass.find(name);
//print("keephand::modasset() find name in asset: ", (existing_asset == ass.end() ? "false" : "true"));
eosio_assert(existing_asset != ass.end(), "name not fund on asset" ); if(existing_asset != ass.end()) { double op_funds = 0.00000000;
if(bsaving) {
op_funds = existing_asset->funds + funds;
} else {
//is enough funds
op_funds = existing_asset->funds - funds - funds * fee_rate;
eosio_assert(op_funds >= , "not enough asset.\n"); auto existing_owner = ass.find(_self);
eosio_assert(existing_owner != ass.end(), " owner user not fund.\n"); ass.modify(*existing_owner, _self , [&]( auto& d ) {
d.name = _self;
d.funds = funds* fee_rate;
});
} ass.modify(*existing_asset, _self , [&]( auto& d ) {
d.name = name;
d.funds = op_funds;
});
}
}
void keephand::delete_asset_data_by_account(const account_name name)
{
asset ass(_self, _self);
auto existing = ass.find(name); if(existing != ass.end()) {
ass.erase(existing);
//print("delete asset name=", name);
}
} void keephand::transfer(const account_name& from, const account_name& to, const double& funds, const std::string& strremark ) {
eosio_assert( from > , "from empty \n");
eosio_assert( to > , "to empty \n");
eosio_assert(funds > , "funds error \n"); require_auth(from); eosio_assert(is_exist_account(to), "fund receiver user failed on userinfo table. \n"); asset ass(_self, _self); auto existing_from = ass.find(from);
eosio_assert(existing_from != ass.end(), "fund sender failed.\n"); double fee = funds * fee_rate;
if(fee < fee_min) {
fee = fee_min;
}
double asset_sender = existing_from->funds - fee - funds;
eosio_assert(asset_sender >= 0.00000000, " sender not enough asset."); funds_option(from, fee + funds, false); auto existing_to = ass.find(to);
eosio_assert(existing_to != ass.end(), "fund receiver failed on asset table\n");
funds_option(to, funds, true ); auto existing_self = ass.find(_self);
eosio_assert(existing_self != ass.end(), "fund self failed on asset table\n");
funds_option(_self, fee, true );
} bool keephand::is_exist_account(const account_name name) {
user acnts(_self, _self);
auto idx = acnts.find(name); return idx != acnts.end();
} void keephand::funds_option(const account_name& name, const double& funds, bool opt) {
asset ass(_self, _self);
auto idx = ass.find(name);
eosio_assert(idx != ass.end(), "fund name failed. \n"); double user_asset = 0.00000000;
if(opt) {
user_asset = idx->funds + funds;
} else {
user_asset = idx->funds - funds;
} eosio_assert(user_asset >= , "asset not enought. \n"); modify_asset(name, user_asset);
} void keephand::modify_asset(const account_name& name, const double& funds) const {
asset ass(_self, _self);
auto idx = ass.find(name);
eosio_assert(idx != ass.end(), "fund name failed on asset table. \n"); ass.modify(*idx, _self , [&]( auto& a ) {
a.name = name;
a.funds = funds;
});
}

二.测试流程:

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,欢迎大家一起交流学习!

EOS 智能合约编写(一)的更多相关文章

  1. 【精解】EOS智能合约演练

    EOS,智能合约,abi,wasm,cleos,eosiocpp,开发调试,钱包,账户,签名权限 热身 本文旨在针对EOS智能合约进行一个完整的实操演练,过程中深入熟悉掌握整个EOS智能合约的流程,过 ...

  2. EOS智能合约存储实例讲解

    EOS智能合约存储实例 智能合约中的基础功能之一是token在某种规则下转移.以EOS提供的token.cpp为例,定义了eos token的数据结构:typedef eos::token<ui ...

  3. EOS智能合约授权限制和数据存储

    EOS智能合约授权限制和数据存储 在EOS合约中,调用合约需要来自账户的授权,同时还要指定需要调用的动作.当然,有的合约并不是所有账户都可以调用的,这就需要用到授权限制.接下来我们就来看看如何限制合约 ...

  4. EOS智能合约开发(四):智能合约部署及调试(附编程示例)

    EOS智能合约开发(一):EOS环境搭建和创建节点 EOS智能合约开发(二):EOS创建和管理钱包 EOS智能合约开发(三):EOS创建和管理账号 部署智能合约的示例代码如下: $ cleos set ...

  5. EOS智能合约开发(三):EOS创建和管理账号

    没有看前面文章的小伙伴可以看一下 EOS智能合约开发(一):EOS环境搭建和启动节点 EOS智能合约开发(二):EOS创建和管理钱包 创建好钱包.密钥之后,接下来你就可以创建账号了,账号是什么?账号保 ...

  6. EOS智能合约开发(二):EOS创建和管理钱包

    上节介绍了EOS智能合约开发之EOS环境搭建及启动节点 那么,节点启动后我们要做的第一件事儿是什么呢?就是我们首先要有账号,但是有账号的前提是什么呢?倒不是先创建账号,而是先要有自己的一组私钥,有了私 ...

  7. eos智能合约与主进程交互

    eos智能合约与主进程交互 1.启动wasm 参考eos智能合约执行流程.md 2.智能合约调用主进程api 如何实现wasm代码与eos宿主交互还需要摸索! 大致:在wasm_interface.c ...

  8. eos智能合约执行流程

    eos智能合约执行 1. 执行流程 controller::push_transaction()  // 事务 -> transaction_context::exec()  // 事务 -&g ...

  9. eos 智能合约开发体验

    eos编译安装 eos 特性 数据存储 eos投票智能合约开发 eos投票智能合约部署测试 注意避坑 eos编译安装 ERROR: Could not find a package configura ...

随机推荐

  1. Cookie是以文本文件保存在客户端的,所以说cookie对象从本质而言是 字符串,所以取值时用字符串,或其数组

  2. codeforces 710A A. King Moves(水题)

    题目链接: A. King Moves 题意: 给出king的位置,问有几个可移动的位置; 思路: 水题,没有思路; AC代码: #include <iostream> #include ...

  3. Chrome Developer Tools之内存分析

    可参考: Chrome Developer Tools之内存分析 http://www.kazaff.me/2014/01/26/chrome-developer-tools%E4%B9%8B%E5% ...

  4. cloudera上面安装Spark2.0

    Cloudera默认值是提供Spark1.6的安装,下面介绍如何来安装spark2.1 1. csd包:http://archive.cloudera.com/spark2/csd/ 2. parce ...

  5. UOJ #348 州区划分 —— 状压DP+子集卷积

    题目:http://uoj.ac/problem/348 一开始可以 3^n 子集DP,枚举一种状态的最后一个集合是什么来转移: 设 \( f[s] \) 表示 \( s \) 集合内的点都划分好了, ...

  6. C#中如何设置日期格式

    在C#中,ToShortDateString()是用于显示短日期格式的方法,如果使用下面的语句: Label1.Text = DateTime.Now.ToShortDateString(); 那么, ...

  7. Jenkins搭建Nodejs自动化测试

    一.安装Jenkins(Windows) 1. 在Jenkins官网(https://jenkins.io/)下载安装包,解压并安装 2. 安装完成后,会自动打开一个页面,根据提示在安装目录下找到随机 ...

  8. Matlab数据类型的转换

    Matlab中有15种基本数据类型,主要是整型.浮点.逻辑.字符.日期和时间.结构数组.单元格数组以及函数句柄等. 1.整型:(int8:uint8:int16:uint16:int32:uint32 ...

  9. 关于android的一些博文收集

    Java网络编程总结 http://www.cnblogs.com/oubo/archive/2012/01/16/2394641.html Android应用系列:双击返回键退出程序 http:// ...

  10. 高级查询子条件查询filter

    Filter Context 在查询过程中,只判断该文档是否满足条件,只有Yes或者No { "query":{ "bool":{ //布尔关键词 " ...