EOS开发实战

时间:2024-04-19 08:19:27

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区块链交互,查阅官方文档可获取更多详细信息。

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开发实战