质押智能合约详解|手把手教你玩转质押合约|Defi质押

本文介绍通过质押底层币(以太坊)资产获取收益的一般逻辑及其实现方法,该方案在很多defi项目得到应用;本文中的收益为ERC20通证,收益获取也可以理解为行为。

开发环境:chrome + metamask + remix
相关发币教学详见:从零教你发行自己的以太坊ERC20智能合约

1 合约主要功能函数介绍

1.1 主要参数
    address private owner;          //合约部署(拥有者)账号地址
    address private profitor;       //收益分配者账号地址,仅该地址有权分配收益
    bool _isDIS = false;            //质押合约功能状态,true才可以进行质押
 
    ERC20 _Token;                   //用于分配收益的ERC20资产
    KeyFlag[] keys;                 //用于标记用户地址的质押状态
 
    uint256 size;                   //质押者地址(用户)数量
    uint256 _maxPledgeAmount;       //最大质押(底层币)额度
    uint256 _maxMiningAmount;       //最大(ERC20收益分配)额度
    uint256 _leftMiningAmount;      //剩余额度
    uint256 _minAmount;             //单次最少质押额度
    uint256 _totalPledegAmount;     //已质押总额度
    uint256 _maxPreMiningAmount;    //最大单次分配额度
    uint256 _startTime;             //开始时间,单位“秒”
    uint256 _endTime;               //结束时间,单位“秒”
    uint256 _precentUp=100;         //与_precentDown一起设定每次收益提取比例
    uint256 _precentDown=100;       //与_precentUp一起设定每次收益提取比例
 
    struct PledgeOrder {            //结构体,用于标记质押用户的各类状态
        bool isExist;               //质押状态
        uint256 token;              //质押额度
        uint256 profitToken;        //收益额度
        uint256 time;               //最近一次提取收益时间
        uint256 index;              //质押地址序号
    }
 
    struct KeyFlag {                //结构体,用于标记用户地址的质押状态
        address key;                //地址
        bool isExist;               //质押状态
    }

部署合约时,构造函数内的参数需要用户输入,以完成相应的参数设置并实现相应功能;

1.2 质押函数pledgeToken
    function pledgeToken() public payable{
        require(address(msg.sender) == address(tx.origin), "no contract");
        require(_isDIS, "is disable");
        require(_leftMiningAmount>0, "less token");
        require(msg.value>=_minAmount, "less token");
        require(_totalPledegAmount.add(msg.value)<=_maxPledgeAmount, "more token");
        require(block.timestamp>=_startTime&&block.timestamp<=_endTime, "is disable");
 
        if(_orders[msg.sender].isExist==false){
            keys.push(KeyFlag(msg.sender,true));
            size++;
            createOrder(msg.value,keys.length.sub(1));
        }else{
            PledgeOrder storage order=_orders[msg.sender];
            order.token=order.token.add(msg.value);
            keys[order.index].isExist=true;
        }
        _totalPledegAmount=_totalPledegAmount.add(msg.value);
    }

功能说明:

  • 明显的,该函数具有接收底层币功能(payable);
  • 质押地址必须是账号地址,不能是合约地址;
  • 需要合约质押功能已经开始,且在活动限定时间内;
  • 剩余额度大于0;
  • 进行质押的底层币额度不能少于最小值,质押后也不能超过限定的最大质押额度;
  • 如果该用户之前没有质押过,则建立档案(createOrder),否则仅修改档案;
1.3 收益分配函数profit
function profit() public onlyProfitor{
        require(_leftMiningAmount>0, "less token");
        require(_totalPledegAmount>0, "no pledge");
        uint256 preToken=_maxPreMiningAmount;
        if(_leftMiningAmount<_maxPreMiningAmount){
            preToken=_leftMiningAmount;
        }
        for(uint i = 0; i < keys.length; i++) {
            if(keys[i].isExist==true){
                PledgeOrder storage order=_orders[keys[i].key];
                order.profitToken=order.profitToken.add(order.token.mul(preToken).div(_totalPledegAmount));
            }
        }
        _leftMiningAmount=_leftMiningAmount.sub(preToken);
    }

功能说明:

  • 必须尚有剩余额度可供分配;
  • 当前质押总额必须大于0;
  • 如果剩余额度足够,则按规则分配设定的每次最大分配额度(_maxPreMiningAmount);
  • 如果剩余额度小于每次最大分配额度,则将剩余额度全部进行分配;根据用户质押数量,平均分配所有额度;
  • 即质押余额越多,收益越多;
1.4 收益提取函数takeProfit
    function takeProfit() public {
        require(address(msg.sender) == address(tx.origin), "no contract");
        require(_orders[msg.sender].profitToken>0,"less token");
        uint256 time=block.timestamp;
        uint256 diff=time.sub(_takeProfitTime[msg.sender]);
        require(diff>86400,"less time");
        PledgeOrder storage order=_orders[msg.sender];
        uint256 takeToken=order.profitToken.mul(_precentUp).div(_precentDown);
        order.profitToken=order.profitToken.sub(takeToken);
        _takeProfitTime[msg.sender]=time;
        _Token.safeTransfer(address(msg.sender),takeToken);
    }

功能说明:

  • 质押地址必须是账号地址,不能是合约地址;
  • 质押地址必须有尚未提取的ERC20收益;
  • 必须距离上次提取时间超过一天(86400秒,该值可以在部署时修改);
  • 通过_precentUp和_precentDown可以设置提取比例,本文示例为100%;
  • 记录本次提取时间并完成资产转账;
  • 注意:必须提前给合约地址转账足够的ERC20资产,否则用户无法成功提取收益;
1.5 本金提取函数takeToken
    function takeToken(uint256 amount) public {
        require(address(msg.sender) == address(tx.origin), "no contract");
        PledgeOrder storage order=_orders[msg.sender];
        require(order.token>0,"no order");
        require(amount<=order.token,"less token");
        _totalPledegAmount=_totalPledegAmount.sub(amount);
        if(order.token==amount){
            order.token=0;
            keys[order.index].isExist=false;
        }else{
            order.token=order.token.sub(amount);
        }
        address payable addr = getPayable(msg.sender);
        addr.transfer(amount);
    }

功能说明:

  • 质押地址必须是账号地址,不能是合约地址;
  • 质押地址必须有尚未提取的本金;
  • 用户可以随时提取本金;
  • 用户可以确定提取本金的额度(少于或等于本人的质押金额度);
  • 完成资产转账
1.6 其它功能函数
    //获取用户质押本金余额
    function getPledgeToken(address tokenAddress) public view returns(uint256) {}
    
    //获取用户收益余额
    function getProfitToken(address tokenAddress) public view returns(uint256) {}
 
    //获取当前质押总额
    function getTotalPledge() public view returns(uint256) {}
 
    //地址转换,允许地址接收资产
    function getPayable(address tokenAddress) private pure returns (address payable) {}
 
    //设置项目开始状态
    function changeIsDIS(bool flag) public onlyOwner {}
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zgsdzczh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值