1. 程式人生 > >EOS(五) - 智慧合約程式碼實踐

EOS(五) - 智慧合約程式碼實踐

    近段時間在探討EOS智慧合約方面的知識,通過程式碼實踐的DAPP能夠更深入的掌握EOS的智慧合約。

    需求如下:

           同事的合作協同工作方式不夠完善, 工作協作方式效率有待提升,公司內部員工激勵方式單一且效果有待提升,需要一種全新的、高效的協同方式來提高工作效率。

    提供兩個類:

         |-create
task -|-commit
         |-confirm

          |-create
token-|-issue 
          |-transfer

 

程式碼如下:


task.hpp

#pragma once

#include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp>

#include <string>

using namespace eosio;
using std::string;

//asset = {int64_t amount; symbol_type symbol}

class token : public contract {
    public:
        token(account_name self) :contract(self){}
        /// @abi action
        void create(account_name issuer,        //誰來發行
                    asset       maximum_supply, //資產資訊:發幣是什麼,發錢多少
                    uint8_t     can_freeze,     //資產能否凍結
                    uint8_t     can_recall,     //是否可以回收
                    uint8_t     can_whitelist); //白名單

        void issue(account_name to, 
				asset quantity, 
				string memo);
        void transfer(account_name from,
                    account_name to,
                    asset quantity,
                    string memo);

    private:
        //friend pdjtask
        inline asset get_supply(symbol_name sym)const;
        inline asset get_balance(account_name owner, symbol_name sym)const;

    private:
        /// @abi table accounts i64
        struct account {
            asset balance;
            uint8_t frozen = 0;
            uint8_t whitelist = 1;

            uint64_t primary_key()const { return balance.symbol.name(); }
        };
        /// @abi table state i64
        struct stat {
            asset        supply;
            asset        max_supply;
            account_name issuer;
            uint8_t      can_freeze = 1;
            uint8_t      can_recall = 1;
            uint8_t      can_whitelist = 1;
            uint8_t      is_frozen =0;
            uint8_t      enforce_whitelist = 0;

            uint64_t primary_key()const { return supply.symbol.name(); };
        };

        typedef eosio::multi_index<N(accounts), account> accounts;
        typedef eosio::multi_index<N(stats), stat> stats;

        void sub_balance(account_name owner, asset value, const stat& st);
        void add_balance(account_name owner, asset value, const stat& st, account_name ram_payer);
};

asset token::get_supply(symbol_name sym) const {
    stats statstable(_self, sym);
    const auto& st = statstable.get(sym);
    return st.supply;
}

asset token::get_balance(account_name owner, symbol_name sym)const {
    accounts accountstable(_self, owner);
    const auto& ac = accountstable.get(sym);
    return ac.balance;
}

class task : public token {
    public:
        task(account_name self): token(self){}

        /// @abi action
        //建立任務
        void createtk(account_name creator, account_name worker, asset taskBonus, string memo);
        //提交任務
        void commit(uint64_t taskID, account_name worker, string memo);
        //驗收任務
        void confirm(uint64_t taskID, account_name creator, uint8_t ok = 1);

    private:
    /// @abi table tasks i64
    struct task {
        uint64_t     taskID; //每個任務的編號
        account_name creator;//任務誰建立
        account_name worker; //任務誰來幹
        asset   bonus;       //懸賞
        uint8_t status = 0;  //任務的狀態
        string  remark;      //描述
        string  comment;     //備註

        uint64_t primary_key()const { return taskID;}
    };

    typedef eosio::multi_index<N(tasks), task> tasks;
};

task.cpp

#include "task.hpp"


void token::sub_balance(account_name owner, asset value, const pdjtoken::stat &st) {
    print("sub_balance->", value.symbol);
    accounts from_acnts(_self, owner);
    //auto fromacc = from_acnts.find(value.symbol.name());

    const auto& from = from_acnts.get(value.symbol.name() );
    eosio_assert(from.balance.amount >= value.amount, "overdrawn balance");

    if( has_auth(owner) ){
        eosio_assert(!st.can_freeze || from.frozen, "account is frozen by issuer");
        eosio_assert(!st.can_freeze || st.is_frozen, "all transfers are frozen by issuer");
        eosio_assert(!st.enforce_whitelist || from.whitelist, "account is not white listed");
    } else if(has_auth(st.issuer)){
        eosio_assert(st.can_recall, "issuer may not recall token");
    } else{
        eosio_assert(false, "insufficient authority");
    }
    print("modify->",from.balance,",",value);

    from_acnts.modify(from, owner, [&](auto& a){
        a.balance -= value;
    });
}

void token::add_balance(account_name owner, asset value, const stat& st, account_name ram_payer)
{
    print("add_balance->", value.symbol);
    accounts to_acnts(_self, owner);
    auto to = to_acnts.find(value.symbol.name());
    if(to == to_acnts.end()){
        //第一次發
        eosio_assert( !st.enforce_whitelist, "can only transfer to white listed accounts");
        to_acnts.emplace( ram_payer, [&](auto& a){
            a.balance = value;
        });
    } else {
        eosio_assert(!st.enforce_whitelist || to->whitelist, "receiver requires whitelist by ...");
        to_acnts.modify(to, 0, [&](auto& a){
            a.balance += value;
        });
    }
}

void token::create(account_name issuer,      
                    asset       maximum_supply, 
                    uint8_t     can_freeze,     
                    uint8_t     can_recall,     
                    uint8_t     can_whitelist)  
{
    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, _self);
    auto existing = statstable.find(sym.name());
    eosio_assert(existing == statstable.end(), "token with symbol already exists");

    //建立表,s:stat
    statstable.emplace(_self, [&](auto& s){
        s.supply.symbol = maximum_supply.symbol;
        s.max_supply    = maximum_supply;
        s.issuer        = issuer;
        s.can_freeze    = can_freeze;
        s.can_recall    = can_recall;
        s.can_whitelist = can_whitelist;
    });
}
//to 要給誰
//quantity 給這個賬戶先打多少錢
//memo 備註
void token::issue(account_name to, asset quantity, string memo)
{
    //print("issue");
    auto sym = quantity.symbol.name();
    print("issue=>",name{sym});
    stats auto& st = statstable.get(sym);

    require_auth(st.issuer);
    eosio_assert(quantity.is_valid(), "invalid quantity");
    eosio_assert(quantity.amount > 0, "must issue positive quantity");
    eosio_assert(quantity <= st.max_supply - st.supply, "quantity exceeds available supply");

    statstable.modify(st, 0, [&](auto& s){
        s.supply += quantity;
    });

    //能夠發行的人,先給它加錢
    add_balance(st.issuer, quantity, st, st.issuer);

    //內部轉賬操作
    if(to != st.issuer) {
        SEND_INL_INE_ACTION(*this, transfer, {st.issuer,N(active)}, {st.issuer, to, quantity, memo});
    }
}

void token::transfer(account_name from, account_name to, asset quantity, string memo)
{
    require_auth( from );
    auto sym = quantity.symbol.name();
    print("transfer",name{sym});

    stats statstable(_self, _self);
    const auto& st = statstable.get(sym);

    require_recipient(from);
    require_recipient(to);

    eosio_assert(quantity.is_valid(), "invalid quantity");
    eosio_assert(quantity.amount > 0, "must transfer positive quantity");

    sub_balance(from, quantity, st);     //我的賬戶減錢
    add_balance(to, quantity, st, from); //他的賬戶加錢
}

void task::createtk(account_name creator, account_name worker, asset taskBonus, string memo)
{
    //建立任務-指定作者-獎金數量-描述
    //檢查creator和worker是否都存在
    require_auth(creator);
    require_auth(worker);

    auto sym = taskBonus.symbol;
    eosio_assert(sym.is_valid(), "invalid symbol name");

    //儲存資料的結構
    tasks tk(_self, _self);
    //建立任務
    tk.emplace(creator, [&](auto &t){
        t.creator = creator;
        t.worker  = worker;
        t.taskID  = tk.available_primary_key(); //主鍵自動增長
        t.bonus   = taskBonus;
        t.remark  = memo;
    });
}

void task::commit(uint64_t taskID, account_name worker, string memo)
{
    //提交任務者必須與任務分配者是一個人
    print("hi",name{_self});
    require_auth(worker);
    tasks tk(_self, _self);
    auto tkobj = tk.find(taskID);
    eosio_assert( tkobj != tk.end(), "taskid not exists");
    eosio_assert( tkobj->worker == worker, "worker not same");
    tk.modify(tkobj, worker, [&](auto &t){
        t.status = 1;
        t.comment = memo;
    });
}

void task::confirm(uint64_t taskID, account_name creator, uint8_t ok)
{
    require_auth(creator);
    tasks tk(_self, _self);
    auto tkobj = tk.find(taskID);
    eosio_assert(tkobj != tk.end(), "taskid not exists");
    uint8_t status = 2;
    if(!ok){
        // re do
        status = 0;
    }
    tk.modify(tkobj, creator, [&](auto &t){
        t.status = status;
        t.comment = "well done!";
    });

    if(ok){
        //發幣刺激
        transfer(creator, tkobj->worker, tkobj->bonus, "very good!");
    }
}

EOSIO_ABI( pdjtask, (createtk)(commit)(create)(issue)(transfer)(confirm) )

 

編譯:
使用eosiocpp編譯

 查看錶:
 cat task.abi

部署:
 cleos set contract maomao ../task/

執行智慧合約:
 1、cleos push action maomao create `{"issuer":"maomao", "maximum_supply":"10000000.00 TOK", "can_freeze":1, "can_recall":1, "can_whitelist":1}` -p maomao
   cleos get table maomao maomao stats
   cleos get table maomao maomao accounts //當前賬戶是沒有錢的

 2、cleos push action maomao issue `{"to":"maomao", "quantity":"1000.00 TOK", "memo":"轉入資金"}` -p maomao