EOS开发实战

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));    
    }
    
});

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




image

猜你喜欢

转载自www.cnblogs.com/tokenpai/p/10320829.html