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. [Swift]LeetCode916.单词子集 | Word Subsets

    We are given two arrays A and B of words.  Each word is a string of lowercase letters. Now, say that ...

  2. Python档案袋(列表、元组、字典、集合 )

    列表 可以同名,有序(通过下标可找到) 取值: 1 #声明列表 2 listx=["L0","L1","L2",33,"L4&qu ...

  3. 死磕 java集合之ArrayList源码分析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可 ...

  4. 『NOIP2018普及组题解』

    标题统计 题目描述 凯凯刚写了一篇美妙的作文,请问这篇作文的标题中有多少个字符? 注意:标题中可能包含大.小写英文字母.数字字符.空格和换行符.统计标题字 符数时,空格和换行符不计算在内. 输入格式 ...

  5. EF实现批量插入

    Z.EntityFramework.BulkInsert EntityFramework 最被人诟病的地方就是它的性能,处理大量数据时的效率.此种条件下,通常会转回使用 ADO.NET 来完成任务.而 ...

  6. JDK1.8源码(三)——java.util.HashMap

      什么是哈希表? 在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能 数组:采用一段连续的存储单元来存储数据.对于指定下标的查找,时间复杂度为O(1):通过给定值进行查找, ...

  7. ImportError: cannot import name UnrewindableBodyError

    错误信息: File "/usr/lib/python2.7/site-packages/urllib3/util/__init__.py", line 4, in <mod ...

  8. linux 远程ssh免密登录

    写在前面 先说说需求: 我们平时开发.运维操作linux过程中经常需要实现将远程文件拷贝到本地或者本地文件拷贝到远程:执行远程命令等操作:这个时候建立ssh免密登录应该是一个比较好的选择: 原理 在l ...

  9. vnc server的安装

    vnc是一款使用广泛的服务器管理软件,可以实现图形化管理.我在安装vnc server碰到一些问题,也整理下我的安装步骤,希望对博友们有一些帮助. 1 安装对应的软件包 [root@centos6 ~ ...

  10. Linux基础知识第五讲,远程管理关机启动网卡命令

    目录 一丶学习目标 二丶关机重启命令 三丶 查看或配置网卡信息 1.网卡与IP地址介绍 2.命令介绍 一丶学习目标 关机/重启 shutdown 查看或配置网卡信息 ifconfig ping 远程登 ...