EOS开发实战


  在上一篇文章《EOS开发入门》中,我们为大家介绍了EOS的节点启动和合约部署和调用等入门知识。本次我们来实现一个复杂的例子,可以为其取一个高大上的名字-悬赏任务管理系统。这可以是我们身边的一个例子,在工作中我们也许会碰到需要周围人帮助实现工作以外的问题,这往往需要靠交情来做到。我们使用eos可以实现这样一个任务管理系统,任务发起人可以发布一个任务给某个赏金猎人,指定任务的赏金。在任务完成后,发起人将约定的赏金交给完成者。

1. 合约设计

  通过故事背景介绍,我们可以察觉我们需要支付赏金的部分,这个可以使用区块链的token来实现。另外还需要一个实现一个对任务的管理,token的实现可以直接使用eosio.token,任务的管理我们需要实现3个action,创建任务,提交任务,验收任务。总结如下:

  • token合约:创建,发行,转账

  • task合约:创建,提交,验收

2. 合约实现

任务需要包含的信息:

struct [[eosio::table]] task {
uint64_t taskID;//任务编号
name creator; //创建者
name worker;//执行者
asset bonus;//承诺奖励
uint8_t status = 0;//状态
string remark;//任务描述
string comment;//评价 uint64_t primary_key()const { return taskID; }
};

2.1 token合约

  • pdjtoken.hpp
/**
* @file pdjtoken.hpp
* @company http://pdjedu.com/
*/
#pragma once #include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp> #include <string> namespace eosiosystem {
class system_contract;
} namespace eosio { using std::string; class [[eosio::contract("pdjtoken")]] pdjtoken : public contract {
public:
using contract::contract;
pdjtoken(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {} [[eosio::action]]
void create( name issuer,
asset maximum_supply); [[eosio::action]]
void issue( name to, asset quantity, string memo ); [[eosio::action]]
void transfer( name from,
name to,
asset quantity,
string memo ); private: inline asset get_supply( name token_contract_account, symbol_code sym_code )const; inline asset get_balance( name token_contract_account, name owner, symbol_code sym_code )const; private: struct [[eosio::table]] account {
asset balance; uint64_t primary_key()const { return balance.symbol.code().raw(); }
}; struct [[eosio::table]] currency_stats {
asset supply;
asset max_supply;
name issuer; uint64_t primary_key()const { return supply.symbol.code().raw(); }
}; typedef eosio::multi_index< "accounts"_n, account > accounts;
typedef eosio::multi_index< "stat"_n, currency_stats > stats; void sub_balance( name owner, asset value );
void add_balance( name owner, asset value, name ram_payer );
}; asset pdjtoken::get_supply( name token_contract_account, symbol_code sym_code )const
{
stats statstable( token_contract_account, sym_code.raw() );
const auto& st = statstable.get( sym_code.raw() );
return st.supply;
} asset pdjtoken::get_balance( name token_contract_account, name owner, symbol_code sym_code )const
{
accounts accountstable( token_contract_account, owner.value );
const auto& ac = accountstable.get( sym_code.raw() );
return ac.balance;
} } /// namespace eosio
  • pdjtoken.cpp
/**
* @file pdjtoken.cpp
* @copyright http://pdjedu.com/
*/ #include "pdjtoken.hpp" namespace eosio { //创建
void pdjtoken::create( name issuer,
asset maximum_supply )
{
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.code().raw() );
auto existing = statstable.find( sym.code().raw() );
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;
});
} //发行
void pdjtoken::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 }
);
}
} //转账
void pdjtoken::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 );
} void pdjtoken::sub_balance( name owner, asset value ) {
accounts from_acnts( _self, owner.value ); const auto& from = from_acnts.get( value.symbol.code().raw(), "no balance object found" );
eosio_assert( from.balance.amount >= value.amount, "overdrawn balance" ); from_acnts.modify( from, owner, [&]( auto& a ) {
a.balance -= value;
});
} void pdjtoken::add_balance( name owner, asset value, name ram_payer )
{
accounts to_acnts( _self, owner.value );
auto to = to_acnts.find( value.symbol.code().raw() );
if( to == to_acnts.end() ) {
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
to_acnts.modify( to, same_payer, [&]( auto& a ) {
a.balance += value;
});
}
} } /// namespace eosio EOSIO_DISPATCH( eosio::pdjtoken, (create)(issue)(transfer) )

2.2 task合约

  • pdjtask.hpp
/**
* @file pdjtask.hpp
* @company http://pdjedu.com/
*/
#pragma once #include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp> #include <string> namespace eosiosystem {
class system_contract;
} namespace eosio { using std::string; class [[eosio::contract]] pdjtask : public contract {
public:
pdjtask(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {} //创建任务
[[eosio::action]]
void createtk( name creator, name worker, asset taskBonus, string memo );
//提交任务
[[eosio::action]]
void commit( uint64_t taskID, string memo );
//验收任务
[[eosio::action]]
void confirm( uint64_t taskID, uint8_t ok = 1 ); private: struct [[eosio::table]] task {
uint64_t taskID;
name creator;
name worker;
asset bonus;
uint8_t status = 0;
string remark;
string comment; uint64_t primary_key()const { return taskID; }
}; typedef eosio::multi_index< "tasks"_n, task > tasks;
private: /*
void transfer( name from,
name to,
asset quantity,
string memo ); */ void task_commit(name from, name to, asset bonus, string memo) { action act = action(
permission_level{from,"active"_n},
"pdjtoken"_n,
"transfer"_n,
std::make_tuple(from, to, bonus, memo)
); act.send();
}
}; } /// namespace eosio
  • pdjtask.cpp
/**
* @file pdjtask.cpp
* @company http://pdjedu.com/
*/ #include "pdjtask.hpp" namespace eosio { //创建任务
void pdjtask::createtk( name creator, name worker, asset taskBonus, string memo )
{
require_auth(creator);
//require_auth(worker); auto sym = taskBonus.symbol;
eosio_assert( sym.is_valid(), "invalid symbol name" ); tasks tk( _code, _code.value ); tk.emplace( creator, [&](auto &t){
t.creator = creator;
t.worker = worker;
t.taskID = tk.available_primary_key();
t.bonus = taskBonus;
t.remark = memo;
});
}
//提交任务
void pdjtask::commit( uint64_t taskID, string memo )
{
//提交任务者必须与任务分配者是一个人
print("hi,",name{_self});
//require_auth(worker);
tasks tk( _code, _code.value );
auto tkobj = tk.find(taskID);
eosio_assert( tkobj != tk.end(), "taskid not exists" );
require_auth(tkobj->worker );
//eosio_assert( tkobj->worker == worker, "worker not same" );
tk.modify(tkobj, tkobj->worker ,[&](auto &t){
t.status = 1;
t.comment = memo;
}); } //验收任务
void pdjtask::confirm( uint64_t taskID, uint8_t ok )
{
//require_auth(creator);
tasks tk( _code, _code.value );
auto tkobj = tk.find(taskID);
eosio_assert( tkobj != tk.end(), "taskid not exists" );
uint8_t status = 2;
print("confirm---",name{tkobj->creator});
require_auth(tkobj->creator);
if ( !ok ) {
// re do
status = 0;
}
tk.modify(tkobj, tkobj->creator, [&](auto &t){
t.status = status;
t.comment = "well done!";
}); if ( ok ){
//发币刺激
//transfer( creator, tkobj->worker, tkobj->bonus, "very good!" );
task_commit(tkobj->creator, tkobj->worker, tkobj->bonus, "very good!");
} } } /// namespace eosio EOSIO_DISPATCH( eosio::pdjtask, (createtk)(commit)(confirm) )

3. eosjs调用合约

  在合约编写完成后,我们还需要编写一个页面来调用智能合约,这就需要用到eosjs。eosjs集成了EOSIO RPC API,用于与EOS区块链交互,查阅官方文档可获取更多详细信息。



monkeysun和laowang已经收到了任务奖励的token

3.1 核心代码

  • index.js
$(function() {
var userAccount;
var userPrivateKey;
$("#LoginWindow").modal(); $(".Login").on("click", function() {
userAccount = $("#userAcc").val();
userPrivateKey = $("#PrivateKey").val(); config = {
chainId: 'cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f', // 32 byte (64 char) hex string
keyProvider: [userPrivateKey], // WIF string or array of keys.. //5J2ZC6ZbtoFnTsZofTnMaTQmZSkdx9DnM9QZbuYTvDS2AgQaGzX
httpEndpoint: 'http://127.0.0.1:8888',
expireInSeconds: 60,
broadcast: true,
verbose: false, // API activity
sign: true
}
eos = Eos(config); eos.getAccount(userAccount).then(result => {
console.log(result);
alert("欢迎回来," + userAccount);
$(".userName span:nth-child(2)").html("账户:" + userAccount);
getBalance();
}).catch(err => {
console.log(err);
alert("错误:账户不存在!");
}); $(".close_win").click();
getTaskList();
}); //发布任务
$(".Createtk").on("click", function() {
console.log("发布任务");
console.log(userPrivateKey);
$("#ReleaseTask").modal();
getTaskList();
});
//确认发布任务
$(".ConfirmRelease").on("click", function() {
var WorkerAcc = $("#GetWorker").val();
var TaskToken = $("#GetToken").val();
var TaskInfo = $("#GetTaskInfo").val();
console.log(WorkerAcc,TaskToken,TaskInfo);
$(".close_win").click(); eos.transaction({
actions: [
{
account: 'pdjtask',
name: 'createtk',
authorization: [{
actor: userAccount,
permission: 'active'
}], data: {
creator: userAccount,
worker: WorkerAcc,
taskBonus: TaskToken,
memo: TaskInfo
}
}
]
}).then(result => {
console.log(result);
alert("发布成功!");
getTaskList();
})
.catch(error => {console.error(error);alert("发生错误!" + error)});
}); //领取任务
$(".Receive").on("click", function() {
console.log("领取任务");
$("#ReceiveTask").modal();
getTaskList();
});
//确认领取
$(".ConfirmReceive").on("click", function() {
var TaskID = $("#ReceiveTaskID").val();
console.log(TaskID);
$(".close_win").click(); eos.transaction({
actions: [
{
account: 'pdjtask',
name: 'receivetk',
authorization: [{
actor: userAccount,
permission: 'active'
}], data: {
taskID: TaskID,
worker: userAccount
}
}
]
}).then(result => {
console.log(result);
alert("领取成功");
getTaskList();
})
.catch(error => {console.error(error);alert("发生错误!" + error)});
}); //提交任务
$(".Commit").on("click", function() {
console.log("提交任务");
$("#SubmitTask").modal();
getTaskList();
});
//确认提交
$(".ConfirmSubmission").on("click", function() { var TaskID = $("#GetTaskID").val();
var TaskInfo = $("#TaskInformation").val();
console.log(TaskInfo);
$(".close_win").click();
eos.transaction({
actions: [
{
account: 'pdjtask',
name: 'commit',
authorization: [{
actor: userAccount,
permission: 'active'
}], data: {
taskID: TaskID,
memo: TaskInfo
}
}
]
}).then(result => { console.log(result);
alert("提交成功");
getTaskList();
})
.catch(error => {console.error(error);alert("发生错误!" + error)});
}); //验收任务
$(".Confirm").on("click", function() {
console.log("验收任务");
$("#ConfirmTask").modal();
getTaskList();
});
//确认验收
$(".TaskConfirm").on("click", function() { var TaskID = $("#taskid").val();
var TaskStatus = $("#TaskStatus").val();
TaskStatus = parseInt(TaskStatus);
console.log(TaskID,TaskStatus);
$(".close_win").click();
eos.transaction({
actions: [
{
account: 'pdjtask',
name: 'confirm',
authorization: [{
actor: userAccount,
permission: 'active'
}], data: {
taskID: TaskID,
ok: TaskStatus
}
}
]
}).then(result => {
console.log(result);
alert("任务验收成功");
getTaskList();
getBalance();
})
.catch(error => {console.error(error);alert("发生错误!" + error)}); }); //查看余额
function getBalance(){
eos.getCurrencyBalance({
account: userAccount,
code: 'pdjtoken',
symbol: 'PTK'
})
.then(result => { console.log(result);
if(result.length == 0)
$(".balance span:nth-child(2)").html("余额:0");
else
$(".balance span:nth-child(2)").html("余额:" + result);
})
.catch(error => console.error(error));
} //console.log(eos); //任务列表
function getTaskList(){
eos.getTableRows({
scope: 'pdjtask',
code: 'pdjtask',
table: 'tasks',
json: true,
lower_bound: 0,
upper_bound: -1,
limit: 20
})
.then(function(result){
console.log(result.rows);
var tr;
var tkStatus = "";
for(var i = 0; i < result.rows.length; i++){ if(result.rows[i].status == 0)
tkStatus = "未领取";
else if(result.rows[i].status == 1)
tkStatus = "已领取";
else if(result.rows[i].status == 2)
tkStatus = "已提交";
else
tkStatus = "已结束"; tr += '<tr>';
tr += '<td class="active">'+result.rows[i].taskID+'</td>';
tr += '<td class="success">'+result.rows[i].creator+'</td>';
tr += '<td class="warning">'+result.rows[i].worker+'</td>';
tr += '<td class="danger">'+result.rows[i].bonus+'</td>';
tr += '<td class="info">'+tkStatus+'</td>';
tr += '<td class="active">'+result.rows[i].remark+'</td>';
tr += '<td class="success">'+result.rows[i].comment+'</td>';
tr += '</tr>'; }
//console.log(tr);
$("#list").html(tr); })
.catch(error => console.error(error));
} });

  本文只包含部分核心代码,点击这里可获取全部代码和文件。以上就是本次分享的内容,欢迎大家学习交流。



EOS开发实战的更多相关文章

  1. EOS开发入门

    EOS开发入门   在上一篇文章<EOS开发环境搭建>中,我们已经完成了EOS开发环境的搭建,本次为大家带来的是EOS开发入门的相关内容. 1. EOS的合约开发基础   智能合约是一种旨 ...

  2. chrome拓展开发实战:页面脚本的拦截注入

    原文请访问个人博客:chrome拓展开发实战:页面脚本的拦截注入 目前公司产品的无线站点已经实现了业务平台组件化,所有业务组件的转场都是通过路由来完成,而各个模块是通过requirejs进行统一管理, ...

  3. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  4. CSS高效开发实战:CSS 3、LESS、SASS、Bootstrap、Foundation --读书笔记(1)设定背景图

    技术的新发展,除计算机可以接入互联网之外,平板电脑.智能手机.智能电视等其他设备均可访问互联网.在多设备时代,构建多屏体验也不是听说的那么难. 但是这也增加了学习CSS的难度?不知道如何上手,只懂一点 ...

  5. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 ——持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  6. 第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容

    第一部分:微信JS-SDK介绍 微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统 ...

  7. 第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...

  8. 第七篇 :微信公众平台开发实战Java版之如何获取微信用户基本信息

    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同). 公众号可通过本接口来根据O ...

  9. 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...

随机推荐

  1. Java-线程池专题 (美团面试题)

    去美团面试,问到了什么是线程池,如何使用,为什么要用,以下做个总结 1.什么是线程池:  java.util.concurrent.Executors提供了一个 java.util.concurren ...

  2. Chapter 5 Blood Type——20

    "Just let me sit for a minute, please?" I begged. “就让我坐一会可以吗?” 我乞求道. He helped me sit on t ...

  3. scrapy爬虫学习系列七:scrapy常见问题解决方案

    1 常见错误 1.1 错误: ImportError: No module named win32api 官方参考:https://doc.scrapy.org/en/latest/faq.html# ...

  4. scrapy爬虫学习系列四:portia的学习入门

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  5. JDK源码分析(5)之 HashMap 相关

    HashMap作为我们最常用的数据类型,当然有必要了解一下他内部是实现细节.相比于 JDK7 在JDK8 中引入了红黑树以及hash计算等方面的优化,使得 JDK8 中的HashMap效率要高于以往的 ...

  6. mybatis和hibernate的区别

    1. hibernate是全自动,而mybatis是半自动 hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql.而mybat ...

  7. [Vue] vue跳转外部链接

    问题 vue 跳转外部链接问题,当跳转的时候会添加在当前地址后面 var url = 'www.baidu.com' //跳转1 window.localtion.href = url //跳转2 w ...

  8. DSAPI多功能组件编程应用-参考-Win32API常数

    DSAPI多功能组件编程应用-参考-Win32API常数 在编程过程中,常常需要使用Win32API来实现一些特定功能,而Win32API又往往需要使用一些API常数,百度搜索常数值,查手册,也就成了 ...

  9. DSAPI多功能组件编程应用-HTTP监听服务端与客户端

    本文中,演示了使用DSAPI.网络相关.HTTP监听,快速建立服务端和客户端. HTTP监听服务端的作用,是监听指定计算机端口,以实现与IIS相同的解析服务,提供客户端的网页请求,当然,这不仅仅是应用 ...

  10. Android开发过程中的坑及解决方法收录(六)

    1. file.listFiles 空指针异常 最近在弄个小项目,类似一个文件管理器,需要获得手机存储里的目录之后显示,但是运行过程中出现错误,搜索了资料,得出了以下的解决办法 问题产生的原因: an ...