在上一篇文章《EOS開發入門》中,咱們爲你們介紹了EOS的節點啓動和合約部署和調用等入門知識。本次咱們來實現一個複雜的例子,能夠爲其取一個高大上的名字-懸賞任務管理系統。這能夠是咱們身邊的一個例子,在工做中咱們也許會碰到須要周圍人幫助實現工做之外的問題,這每每須要靠交情來作到。咱們使用eos能夠實現這樣一個任務管理系統,任務發起人能夠發佈一個任務給某個賞金獵人,指定任務的賞金。在任務完成後,發起人將約定的賞金交給完成者。html
經過故事背景介紹,咱們能夠察覺咱們須要支付賞金的部分,這個能夠使用區塊鏈的token來實現。另外還須要一個實現一個對任務的管理,token的實現能夠直接使用eosio.token,任務的管理咱們須要實現3個action,建立任務,提交任務,驗收任務。總結以下:ios
token合約:建立,發行,轉帳git
task合約:建立,提交,驗收github
任務須要包含的信息:json
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; } };
/** * @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
/** * @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) )
/** * @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
/** * @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) )
在合約編寫完成後,咱們還須要編寫一個頁面來調用智能合約,這就須要用到eosjs。eosjs集成了EOSIO RPC API,用於與EOS區塊鏈交互,查閱官方文檔可獲取更多詳細信息。ide
monkeysun和laowang已經收到了任務獎勵的token學習
$(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)); } });
本文只包含部分核心代碼,點擊這裏可獲取所有代碼和文件。以上就是本次分享的內容,歡迎你們學習交流。區塊鏈