Token staking
代码未经验证 实际使用还需要测试
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Pool is Ownable { //质押token地址 IERC20 stakeToken; //质押奖励token地址 IERC20 rewardToken; //每分钟产出奖励数量 uint256 rewardPerMin; //某地址的质押份额 mapping(address => uint256) private shares; //某地址已经提现的奖励 mapping(address => uint256) private withdrawdReward; //某地址上一次关联的每份额累计已产出奖励 mapping(address => uint256) private lastAddUpRewardPerShare; //某地址最近一次关联的累计已产出总奖励 mapping(address => uint256) private lastAddUpReward; //每份额累计总奖励 uint256 addUpRewardPerShare; //总挖矿奖励数量 uint256 totalReward; //累计份额 uint256 totalShares; //最近一次(如果没有最近一次则是首次)挖矿区块时间,秒 uint256 lastBlockT; //最近一次(如果没有最近一次则是首次)每份额累计奖励 uint256 lastAddUpRewardPerShareAll; //构造函数 constructor(address _stakeTokenAddr, address _rewardTokenAddr, uint256 _rewardPerMin){ stakeToken = IERC20(_stakeTokenAddr); rewardToken = IERC20(_rewardTokenAddr); rewardPerMin = _rewardPerMin; } //质押,【外部调用/所有人/不需要支付/读写状态】 /// @notice 1. msg.sender转入本合约_amount数量的质押token /// @notice 4. 记录此时msg.sender已经产出的总奖励 /// @notice 2. 增加msg.sender等量的质押份额 /// @notice 3. 计算此时每份额累计总产出奖励 function stake(uint256 _amount) external { stakeToken.transferFrom(msg.sender, this.address, _amount); uint256 currenTotalRewardPerShare = getRewardPerShare(); lastAddUpReward[msg.sender] += (currenTotalRewardPerShare - lastAddUpRewardPerShare[msg.sender]) * shares[msg.sender]; shares[msg.sender] += _amount; updateTotalShare(_amount, 1); lastAddUpRewardPerShare[msg.sender] = currenTotalRewardPerShare; } //解除质押,提取token,【外部调用/所有人/不需要支付/读写状态】 /// @notice 1. _amount必须<=已经质押的份额 /// @notice 4. 记录此时msg.sender已经产出的总奖励 function unStake(uint256 _amount) external { require(_amount <= shares[msg.sender], "UNSTAKE_AMOUNT_MUST_LESS_SHARES"); stakeToken.transferFrom(this.address, msg.sender, _amount); uint256 currenTotalRewardPerShare = getRewardPerShare(); lastAddUpReward[msg.sender] += (currenTotalRewardPerShare - lastAddUpRewardPerShare[msg.sender]) * shares[msg.sender]; shares[msg.sender] -= _amount; updateTotalShare(_amount, 2); lastAddUpRewardPerShare[msg.sender] = currenTotalRewardPerShare; } //更新质押份额,【内部调用/合约创建者/不需要支付】 /// @param _amount 更新的数量 /// @param _type 1增加,其他 减少 /// @notice 每次更新份额之前,先计算之前的份额累计奖励 function updateTotalShare(uint256 _amount, uint256 _type) internal onlyOwner { lastAddUpRewardPerShareAll = getRewardPerShare(); lastBlockT = block.timestamp; if(_type == 1){ totalShares += _amount; } else{ totalShares -= _amount; } } //获取截至当前每份额累计产出,【内部调用/合约创建者/不需要支付/只读】 /// @notice 1.(当前区块时间戳-具体当前最近一次计算的时间戳) * 每分钟产出奖励 / 60秒 / 总份额 + 距离当前最近一次计算的时候的每份额累计奖励 = 当前每份额累计奖励 /// @notice 2. 更新最近一次计算每份额累计奖励的时间和数量 function getRewardPerShare() internal view onlyOwner returns(uint256) { return (block.timestamp - lastBlockT) * rewardPerMin / 60 / totalShares + lastAddUpRewardPerShareAll; } //计算累计奖励,【内部调用/合约创建者/不需要支付/只读】 /// @notice 仅供内部调用,统一计算规则 function getaddupReword(address _address) internal onlyOwner view returns(uint256) { return lastAddUpReward[_address] + ((getRewardPerShare() - lastAddUpRewardPerShare[_address]) * shares[_address]); } //计算可提现奖励,【内部调用/合约创建者/不需要支付/只读】 /// @notice 仅供内部调用,统一计算规则 function getWithdrawdReword(address _address) internal onlyOwner view returns(uint256) { return lastAddUpReward[_address] + ((getRewardPerShare() - lastAddUpRewardPerShare[_address]) * shares[_address]) - withdrawdReward[_address]; } //提现收益,【外部调用/所有人/不需要支付/读写】 /// @notice 1. 计算截至到当前的累计获得奖励 /// @notice 2. _amount必须<=(累计获得奖励-已提现奖励) /// @notice 3. 提现,提现需要先增加数据,再进行提现操作 function withdraw(uint256 _amount) external { require(_amount <= getWithdrawdReword(msg.sender), "WITHDRAW_AMOUNT_LESS_ADDUPREWARD"); withdrawdReward[msg.sender] += _amount; rewardToken.transferFrom(this.address, msg.sender, _amount); } //获取可提现奖励,【外部调用/所有人/不需要支付】 function withdrawdReword() external view returns(uint256) { return getWithdrawdReword(msg.sender); } //获取已提现奖励,【外部调用/所有人/不需要支付】 function hadWithdrawdReword() external view returns(uint256) { return withdrawdReward[msg.sender]; } //获取累计奖励,【外部调用/所有人/不需要支付】 function addupReword() external view returns(uint256) { return getaddupReword(msg.sender); } //获取质押份额,【外部调用/所有人/不需要支付/只读】 function getShare() external view returns(uint256) { return shares[msg.sender]; } }
https://mirror.xyz/daxiong.eth/oo9YlraYSCZnlUsmRCY4KFHZlDrJwdnILvr7xrvdI_Y