# Simple Token Farming Contract

{% code title="examplefarm.hpp" %}

````cpp
#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

```
````

{% endcode %}

{% code title="examplefarm.cpp" %}

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

```
````

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.vexanium.com/reference/smart-contract/c++-smart-contract/simple-token-farming-contract.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
