Simple Token Farming Contract

examplefarm.hpp
#pragma once
#include <eosiolib/asset.hpp>
#include <eosiolib/eosio.hpp>
#include <eosiolib/singleton.hpp>
#include <eosiolib/transaction.hpp>

#define COBAADMIN "cobafinances" //account smart contract
#define COBASTAKE "cobstakepool" //account stake pool
#define COBADRAW "cobdrawpools"  //account reward

#define CBPTOKEN "cbptvextoken" //account token CBP
#define CPPTTOKEN "cobapointvex" //account token CPPT stake
#define VEXTOKEN "vex.token" //account token VEX stake

#define CBP_SYMBOL eosio::symbol("CBP", 8) //CBP decimal 8
#define CPPT_SYMBOL eosio::symbol("CPPT", 5) //CPPT decimal 5
#define VEX_SYMBOL eosio::symbol("VEX", 4) //VEX decimal 4


#define NO_TIME   2000000000
#define SECONDS_THREE_DAY  259200
#define MIN_STAKE_CPPT 1000000 //100 CPPT decimal 4
#define MAX_STAKE_CPPT eosio::asset(40000000000000000, CPPT_SYMBOL)
#define MIN_STAKE_VEX 1000 //0.1 VEX 
#define MAX_STAKE_VEX eosio::asset(40000000000000000, VEX_SYMBOL)

#define MAX_UNSTAKE_CPPT 100000000 //10K
#define MAX_UNSTAKE_VEX 100000000 //10K

// aspt => cbp
// aptt => cppt

#define CBP_VEX_STAGE_MINING eosio::asset(30000000000000, CBP_SYMBOL)
//250ribu decimal 8 token CBP

using eosio::extended_asset;
using namespace eosio;
 
static constexpr int64_t max_amount  = 10000000000000000;

namespace vexdt {
    class [[eosio::contract]] examplefarm :public eosio::contract {
    public:
        examplefarm(eosio::name receiver, eosio::name code, eosio::datastream<const char *> ds) :
                eosio::contract(receiver, code, ds),
                _global(eosio::name(COBAADMIN), eosio::name(COBAADMIN).value),
                _users(eosio::name(COBAADMIN), eosio::name(COBAADMIN).value){
        }

        //更新抵押
        ACTION doissue(const uint64_t &idfrom, const uint64_t &idto);

        ACTION init(); 

        ACTION setstop(const uint8_t &state);

        ACTION claim(const eosio::name &from, const std::string &pooltype);

        ACTION exit(const eosio::name &from, const std::string &pooltype);


        bool iscppttoken(const eosio::extended_asset &quantity)
        {
            if ((quantity.contract == eosio::name(CPPTTOKEN)) && (quantity.quantity.symbol == CPPT_SYMBOL))
            {
                return true;
            }
            return false;
        }

        bool isvextoken(const eosio::extended_asset &quantity)
        {
            if ((quantity.contract == eosio::name(VEXTOKEN)) && (quantity.quantity.symbol == VEX_SYMBOL))
            {
                return true;
            }
            return false;
        }


        void apply(eosio::name code, eosio::name action);

        void stake(const eosio::name &from,
                const eosio::asset &quantity);

        void onTransfer(const eosio::name &from,
            const eosio::name &to,
            const eosio::extended_asset &quantity,
            const std::string &memo);

        TABLE global {
            uint8_t initState;
            uint8_t stopState;

            uint64_t checkfromid;
            uint64_t maxstakeid;
            uint64_t check_update_time_doissue;

            eosio::asset all_pool_reward;//单倍池奖励

            eosio::asset total_staked_cppt;
            eosio::asset total_staked_vex;
            eosio::asset cbp_pool_reward;
            eosio::asset cbp_pool_day_reward;
            eosio::asset vex_pool_reward;
            eosio::asset vex_pool_day_reward;

            eosio::asset total_mininged_cbp_vex;
        };
        typedef eosio::singleton<"global"_n, global> global_table;


        TABLE st_user {
            uint64_t    id = 0;
            eosio::name holder;
            eosio::asset cppt_stake;
            eosio::asset vex_stake;
            uint64_t cppt_update_time;
            uint64_t vex_update_time;
            uint8_t everymonth = 0;

            eosio::asset cppt_bonus;
            eosio::asset vex_bonus;

            uint64_t primary_key() const { return id; }
            uint64_t by_holder() const { return holder.value; }
        };

        typedef eosio::multi_index<"user"_n, st_user,
                                eosio::indexed_by<"holder"_n, eosio::const_mem_fun<st_user, uint64_t, &st_user::by_holder>>
                                    > user_table;

        global_table _global;
        user_table _users;

        ACTION test()
        {
            require_auth(eosio::name(COBAADMIN));

           _global.remove( );
            auto itr = _users.begin();
            while(itr != _users.end()){
                itr = _users.erase(itr);
            }
        }
    };


    struct st_transfer
    {
        eosio::name from;
        eosio::name to;
        eosio::asset quantity;
        std::string memo;
    };

    void examplefarm::apply(eosio::name code, eosio::name action)
    {
        auto &thiscontract = *this;

        if (action == eosio::name("transfer") && (code == eosio::name(CPPTTOKEN) || code == eosio::name(VEXTOKEN)) )
        {
            auto transfer_data = eosio::unpack_action_data<st_transfer>();
            onTransfer(transfer_data.from, transfer_data.to, eosio::extended_asset(transfer_data.quantity, code), transfer_data.memo);
            return;
        }
        
        if (code != eosio::name(COBAADMIN))
            return;
        if( code == eosio::name(COBAADMIN) ) {
            switch(action.value) {
                case eosio::name("init").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::init); 
                    break;
                case eosio::name("setstop").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::setstop); 
                    break;
                case eosio::name("test").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::test); 
                    break;
                case eosio::name("doissue").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::doissue); 
                    break; 
                case eosio::name("claim").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::claim); 
                    break;
                case eosio::name("exit").value: 
                    execute_action(eosio::name(COBAADMIN), eosio::name(code), &examplefarm::exit); 
                    break;
            }
        }
    }


    extern "C"
    {
        [[noreturn]] void apply(uint64_t receiver, uint64_t code, uint64_t action) {
            eosio::datastream<const char*> ds( nullptr, 0 );
            examplefarm p(eosio::name(receiver), eosio::name(code), ds);
            p.apply(eosio::name(code), eosio::name(action));
            eosio_exit(0);
        }
    }

} /// namespace eosio

```
examplefarm.cpp
#include "examplefarm.hpp"

namespace vexdt {
    void examplefarm::stake(const eosio::name &from, const eosio::asset &quantity) {

        auto global = _global.get_or_default();
        eosio_assert(global.stopState == 0, "In stop state!");

        if (from == eosio::name(COBAADMIN) || from == eosio::name(CPPTTOKEN) || from == eosio::name(VEXTOKEN)) {
            return;
        }
        require_auth( from );

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

        bool iscppt = false;
        bool isvex = false;

        if(quantity.symbol == CPPT_SYMBOL)
        {
            iscppt = true;
        }
        else if(quantity.symbol == VEX_SYMBOL)
        {
            isvex = true;
        }

        auto indexholder = _users.get_index<"holder"_n>();
        auto itr = indexholder.lower_bound(from.value);
        if (itr->holder.value != from.value)
        {
            _users.emplace(eosio::name(COBAADMIN), [&](auto &r) {
                    r.id = _users.available_primary_key();   
                    r.holder = from;

                    r.cppt_stake = iscppt ? quantity : eosio::asset(0, CPPT_SYMBOL);
                    r.cppt_update_time = now();

                    r.vex_stake = isvex ? quantity : eosio::asset(0,VEX_SYMBOL);
                    r.vex_update_time = now();
                    
                    r.cppt_bonus = eosio::asset(0, CBP_SYMBOL);
                    r.vex_bonus = eosio::asset(0, CBP_SYMBOL);
                    
                    auto global = _global.get_or_default();
                    if(iscppt)
                    {
                        global.total_staked_cppt += quantity;
                    }
                    else if(isvex)
                    {
                        global.total_staked_vex += quantity;
                    }
                    global.maxstakeid = r.id;
                    _global.set(global,eosio::name(COBAADMIN));                  
                    });
        } else {             
            auto  itrmd = _users.find(itr->id);
            _users.modify(itrmd,eosio::name(COBAADMIN), [&](auto &r) 
            {
                    if(iscppt)
                    {
                        r.cppt_stake += quantity;
                    }
                    else if(isvex)
                    {
                        r.vex_stake += quantity;
                    }

                    auto global = _global.get_or_default();
                    if(iscppt)
                    {
                        global.total_staked_cppt += quantity;
                    }
                    else if(isvex)
                    {
                        global.total_staked_vex += quantity;
                    }
                    
                    _global.set(global,eosio::name(COBAADMIN)); 
            }); 
        }
    }

    void examplefarm::claim(const eosio::name &from, const std::string &pooltype) {

        auto global = _global.get_or_default();
        eosio_assert(global.stopState == 0, "In stop state!");
        require_auth( from );

        auto indexholder = _users.get_index<"holder"_n>();
        auto itr = indexholder.lower_bound(from.value);
        eosio_assert(itr->holder.value == from.value, "sorry, no bonus for you."); 

        auto available_darw_balance = eosio::asset(0, CBP_SYMBOL);
    
        if(pooltype == "CPPT")
        {
            available_darw_balance = itr->cppt_bonus;
        }
        else if(pooltype == "VEX")
        {
            available_darw_balance = itr->vex_bonus;
        }

        eosio_assert(available_darw_balance.amount > 0, "already drawed.");
        eosio_assert(available_darw_balance.amount < max_amount, "trandfer bonus amount overflow !");

        auto  itrmd = _users.find(itr->id);
        _users.modify(itrmd,eosio::name(COBAADMIN), [&](auto &r) 
        {
            auto zerobalance = eosio::asset(0, CBP_SYMBOL);
            if(pooltype == "CPPT")
            {
                r.cppt_bonus = zerobalance;
            }
            else if(pooltype == "VEX")
            {
                r.vex_bonus = zerobalance;
            }
        });

        action(
                permission_level{ eosio::name(COBADRAW), eosio::name("active")},
                eosio::name(CBPTOKEN),
                eosio::name("transfer"),
                std::make_tuple(eosio::name(COBADRAW),from, available_darw_balance, std::string("claim bonus"))
            ).send();
        
    }

    void examplefarm::exit(const eosio::name &from, const std::string &pooltype) {

        auto global = _global.get_or_default();
        eosio_assert(global.stopState == 0, "In stop state!");
        require_auth( from );

        auto indexholder = _users.get_index<"holder"_n>();
        auto itr = indexholder.lower_bound(from.value);
        eosio_assert(itr->holder.value == from.value, "sorry, no bonus for you."); 

        bool iscppt = false;
        bool isvex = false;

        auto available_darw_balance = eosio::asset(0, CBP_SYMBOL);

        auto paybacktoken = eosio::asset(0, CPPT_SYMBOL);
        auto afterunstake = eosio::asset(0, CPPT_SYMBOL);
        auto nowtime = now();

        if(pooltype == "CPPT")
        {
            available_darw_balance = itr->cppt_bonus;
     
            paybacktoken = itr->cppt_stake;
          
            iscppt = true;
        }
        else if(pooltype == "VEX")
        {
            available_darw_balance = itr->vex_bonus;
    
            paybacktoken = itr->vex_stake;
         
            isvex = true;
        }

        eosio_assert(available_darw_balance.amount < max_amount, "trandfer bonus amount overflow !");

        if(pooltype == "CPPT")
        {
            global.total_staked_cppt -= paybacktoken;
            eosio_assert(global.total_staked_cppt.amount < max_amount, "total_staked_cppt amount overflow !");
        }
        else if(pooltype == "VEX")
        {
            global.total_staked_vex -= paybacktoken;
            eosio_assert(global.total_staked_vex.amount < max_amount, "total_staked_vex amount overflow !");
        }
        
        _global.set(global,eosio::name(COBAADMIN)); 


        //返还抵押币
        if(paybacktoken.amount > 0)
        {
            action(permission_level{eosio::name(COBASTAKE), eosio::name("active")},
                eosio::name(CPPTTOKEN),
                eosio::name("transfer"),
                std::make_tuple(eosio::name(COBASTAKE), from, paybacktoken, std::string("get back 5 percent staked asset")))
                .send();
        }

        if(available_darw_balance.amount > 0)
        {
            action(
                permission_level{ eosio::name(COBADRAW), eosio::name("active")},
                eosio::name(CBPTOKEN),
                eosio::name("transfer"),
                std::make_tuple(eosio::name(COBADRAW),from, available_darw_balance, std::string("claim bonus"))
            ).send();
        }

        auto  itrmd = _users.find(itr->id);
        _users.modify(itrmd,eosio::name(COBAADMIN), [&](auto &r) 
        {
            auto zerobalance = eosio::asset(0, CBP_SYMBOL);
            if(iscppt)
            {
                r.cppt_bonus = zerobalance;
                r.cppt_update_time = now();
                r.cppt_stake = eosio::asset(0, CPPT_SYMBOL);
            }
            else if(isvex)
            {
                r.cppt_bonus = eosio::asset(0, VEX_SYMBOL);
                r.vex_update_time = now();
                r.vex_stake = eosio::asset(0, VEX_SYMBOL);
            }


        });
    }

    void examplefarm::doissue(const uint64_t &idfrom, const uint64_t &idto)
    {
        require_auth2(capi_name(eosio::name(COBAADMIN).value), capi_name(eosio::name("cron").value));
        auto global = _global.get_or_default();
        eosio_assert(global.stopState == 0 , "be stop already!");

        //判断阶段挖矿是否已挖完
        eosio_assert(global.total_mininged_cbp_vex < CBP_VEX_STAGE_MINING, "The current mining has finished!");
        
        //判断按顺序执行
        eosio_assert(global.checkfromid == idfrom, "front position not finish yet!");
        if(idfrom == 0) 
        {
            auto nowtime = now();
            //120s 110s
            eosio_assert(nowtime - global.check_update_time_doissue > 120, "have update recently!");
            global.check_update_time_doissue = nowtime; 
        }
        
        auto itrbegin = _users.find(idfrom);
        eosio_assert(itrbegin != _users.end(), "id not found!");
        auto itrend = _users.find(idto);
        eosio_assert(itrend != _users.end(), "id not found!");

        for (uint64_t itrid = idfrom; itrid <= idto; ++itrid) 
        {
            auto itr = _users.find(itrid);
            if(itr != _users.end())
            {
                auto userbonus_cbp = eosio::asset(0, CBP_SYMBOL);
                auto userbonus_vex = eosio::asset(0, CBP_SYMBOL);
               
                if(global.total_staked_cppt.amount > 0)
                {
                    double total = global.total_staked_cppt.amount;
                    double mystake = itr->cppt_stake.amount;
                    userbonus_cbp = global.cbp_pool_reward;
                    userbonus_cbp.amount *= (mystake/total);
                }
                if(global.total_staked_vex.amount > 0)
                {
                    double total = global.total_staked_vex.amount;
                    double mystake = itr->vex_stake.amount;
                    userbonus_vex = global.vex_pool_reward;
                    userbonus_vex.amount *= (mystake/total);
                }

                
                auto  itrmd = _users.find(itr->id);

                auto leftmining = CBP_VEX_STAGE_MINING - global.total_mininged_cbp_vex;

                if(userbonus_cbp + userbonus_vex >= leftmining)
                {
                    userbonus_vex = (leftmining - userbonus_cbp);
                }
                


                auto usermining = userbonus_vex + userbonus_cbp;

                _users.modify(itrmd,eosio::name(COBAADMIN), [&](auto &r) 
                {
                    r.cppt_bonus += userbonus_cbp;
                    r.vex_bonus += userbonus_vex;
                    global.total_mininged_cbp_vex += (userbonus_cbp + userbonus_vex);
                });

                if(usermining >= leftmining)
                {
                    break;
                }
            }
        }

        //更新正在处理的id
        global.checkfromid = idto + 1;
        if(global.checkfromid > global.maxstakeid)
        {
            global.checkfromid = 0;
        }
        _global.set(global, eosio::name(COBAADMIN));
    }

    void examplefarm::onTransfer(const eosio::name &from,
            const eosio::name &to,
            const eosio::extended_asset &quantity,
            const std::string &memo)
    {
        auto global = _global.get_or_default();
        eosio_assert(global.stopState == 0, "In stop state!");
        eosio_assert((global.total_staked_cppt < MAX_STAKE_CPPT), "Maximum stack reached");
        eosio_assert((global.total_staked_vex < MAX_STAKE_VEX), "Maximum stack reached");

        require_auth(from);

        eosio::action act = eosio::get_action( 1, 0 );
     
        eosio_assert( (act.account == eosio::name("vexcore")) || (act.name== eosio::name("transfer") && (act.account == eosio::name(CPPTTOKEN) || act.account == eosio::name(VEXTOKEN)))  ," Human only! ");

        if((to == eosio::name(COBAADMIN)) && (memo.substr(0, 5) == "stake") )
        {
            bool iscppt = iscppttoken(quantity);
            bool isvex = isvextoken(quantity);
          
            eosio_assert(iscppt || isvex," please use CPPT or VEX token ");
            


            if(iscppt) eosio_assert((quantity.quantity.amount >= MIN_STAKE_CPPT), "Stake quantity must be greater than minimum");
            if(isvex) eosio_assert((quantity.quantity.amount >= MIN_STAKE_VEX), "Stake quantity must be greater than minimum");
           
            stake(from,quantity.quantity);

            action(permission_level{eosio::name(COBAADMIN), eosio::name("active")},
                quantity.contract,
                eosio::name("transfer"),
                std::make_tuple(eosio::name(COBAADMIN), eosio::name(COBASTAKE), quantity.quantity, std::string("to coldwallet all stake asset")))
                .send();
        }
    }

    void examplefarm::setstop(const uint8_t &state)
    {
        require_auth2(capi_name(eosio::name(COBAADMIN).value), capi_name(eosio::name("cron").value));
        eosio_assert(state == 0 || state == 1, "set a wrong state!");
        auto global = _global.get_or_default();
        global.stopState = state;
        _global.set(global,eosio::name(COBAADMIN));
    }

    void examplefarm::init() 
    {
        require_auth(eosio::name(COBAADMIN));
        auto global = _global.get_or_default();
        eosio_assert(global.initState != 1, "have init already!");

        eosio::asset all = eosio::asset(100000000, CBP_SYMBOL);//120s 一次

        //vex usdv vyn djv 7000 2000 500 500 14 4 1 1 
        global.initState = 1;
        global.stopState = 0;

        global.checkfromid = 0;
        global.maxstakeid = 0;
        global.check_update_time_doissue = now();
        global.all_pool_reward = all;

        eosio::asset one = all/20;
        global.total_staked_cppt = eosio::asset(0, CPPT_SYMBOL);//总抵押
        global.total_staked_vex = eosio::asset(0, VEX_SYMBOL);//总抵押

        global.cbp_pool_reward = one * 76; //kalo dikali jumlah 2menit dlm 3 bulan, kalikan dengan 65745
        global.cbp_pool_day_reward = one * 24 * 30 * 7716 / 100; //日奖池

        global.vex_pool_reward = one * 15; //kalo dikali jumlah 2menit dlm 3 bulan, kalikan dengan 65745
        global.vex_pool_day_reward = one * 24 * 30 * 1543 / 100; //日奖池


        global.total_mininged_cbp_vex = eosio::asset(0, CBP_SYMBOL);

        _global.set(global, eosio::name(COBAADMIN));
    }
};

```

Last updated