简化stake代码
This commit is contained in:
parent
f21187256e
commit
063a4a775c
@ -1,84 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity 0.8.19;
|
|
||||||
|
|
||||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
||||||
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
||||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
||||||
|
|
||||||
import {IRewardDistributor} from "./interfaces/IRewardDistributor.sol";
|
|
||||||
import {IRewardTracker} from "./interfaces/IRewardTracker.sol";
|
|
||||||
import {Governable} from "../core/Governable.sol";
|
|
||||||
|
|
||||||
contract RewardDistributor is IRewardDistributor, ReentrancyGuard, Governable {
|
|
||||||
using SafeERC20 for IERC20;
|
|
||||||
|
|
||||||
address public override rewardToken;
|
|
||||||
uint256 public override tokensPerInterval;
|
|
||||||
uint256 public lastDistributionTime;
|
|
||||||
address public rewardTracker;
|
|
||||||
|
|
||||||
address public admin;
|
|
||||||
|
|
||||||
event Distribute(uint256 amount);
|
|
||||||
event TokensPerIntervalChange(uint256 amount);
|
|
||||||
|
|
||||||
modifier onlyAdmin() {
|
|
||||||
require(msg.sender == admin, "RewardDistributor: forbidden");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(address _rewardToken, address _rewardTracker) {
|
|
||||||
rewardToken = _rewardToken;
|
|
||||||
rewardTracker = _rewardTracker;
|
|
||||||
admin = msg.sender;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAdmin(address _admin) external onlyGov {
|
|
||||||
admin = _admin;
|
|
||||||
}
|
|
||||||
|
|
||||||
// to help users who accidentally send their tokens to this contract
|
|
||||||
function withdrawToken(address _token, address _account, uint256 _amount) external onlyGov {
|
|
||||||
IERC20(_token).safeTransfer(_account, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLastDistributionTime() external onlyAdmin {
|
|
||||||
lastDistributionTime = block.timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTokensPerInterval(uint256 _amount) external onlyAdmin {
|
|
||||||
require(lastDistributionTime != 0, "RewardDistributor: invalid lastDistributionTime");
|
|
||||||
IRewardTracker(rewardTracker).updateRewards();
|
|
||||||
tokensPerInterval = _amount;
|
|
||||||
emit TokensPerIntervalChange(_amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pendingRewards() public view override returns (uint256) {
|
|
||||||
if (block.timestamp == lastDistributionTime) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 timeDiff = block.timestamp - lastDistributionTime;
|
|
||||||
return tokensPerInterval * timeDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
function distribute(uint256 _amount, uint256 _decimals) external override returns (uint256) {
|
|
||||||
require(msg.sender == rewardTracker, "RewardDistributor: invalid msg.sender");
|
|
||||||
uint256 amount = pendingRewards();
|
|
||||||
if (amount == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastDistributionTime = block.timestamp;
|
|
||||||
|
|
||||||
uint256 tokenAmount = amount * _amount / (10**_decimals);
|
|
||||||
|
|
||||||
uint256 balance = IERC20(rewardToken).balanceOf(address(this));
|
|
||||||
require(tokenAmount <= balance, "RewardDistributor: insufficient balance");
|
|
||||||
|
|
||||||
IERC20(rewardToken).safeTransfer(msg.sender, tokenAmount);
|
|
||||||
|
|
||||||
emit Distribute(tokenAmount);
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
}
|
|
@ -48,7 +48,7 @@ contract RewardRouter is ReentrancyGuard, Governable {
|
|||||||
// check if the user has staked CEC in the vester
|
// check if the user has staked CEC in the vester
|
||||||
if (cecVester != address(0) && IVester(cecVester).needCheckStake()) {
|
if (cecVester != address(0) && IVester(cecVester).needCheckStake()) {
|
||||||
IVester(cecVester).updateVesting(msg.sender);
|
IVester(cecVester).updateVesting(msg.sender);
|
||||||
require(IERC20(cecVester).balanceOf(msg.sender) + _amount <= IRewardTracker(stakedCecTracker).depositBalances(msg.sender, cec), "RewardRouter: insufficient CEC balance");
|
require(IERC20(cecVester).balanceOf(msg.sender) + _amount <= IRewardTracker(stakedCecTracker).stakedAmounts(msg.sender), "RewardRouter: insufficient CEC balance");
|
||||||
}
|
}
|
||||||
_unstakeCec(msg.sender, cec, _amount);
|
_unstakeCec(msg.sender, cec, _amount);
|
||||||
}
|
}
|
||||||
|
@ -5,67 +5,46 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|||||||
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||||
|
|
||||||
import {IRewardDistributor} from "./interfaces/IRewardDistributor.sol";
|
|
||||||
import {IRewardTracker} from "./interfaces/IRewardTracker.sol";
|
import {IRewardTracker} from "./interfaces/IRewardTracker.sol";
|
||||||
import {Governable} from "../core/Governable.sol";
|
import {Governable} from "../core/Governable.sol";
|
||||||
|
|
||||||
contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable {
|
contract RewardTracker is ReentrancyGuard, IRewardTracker, Governable {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
uint256 public constant BASIS_POINTS_DIVISOR = 10000;
|
|
||||||
uint256 public constant PRECISION = 1e30;
|
|
||||||
|
|
||||||
bool public isInitialized;
|
|
||||||
|
|
||||||
string public name;
|
|
||||||
string public symbol;
|
|
||||||
uint8 public decimals = 18;
|
|
||||||
uint256 public override totalSupply;
|
|
||||||
mapping(address account => uint256 amount) public balances;
|
|
||||||
mapping(address owner => mapping(address spender => uint256 amount)) public allowance;
|
|
||||||
|
|
||||||
address public distributor;
|
|
||||||
mapping(address token => bool status) public isDepositToken;
|
mapping(address token => bool status) public isDepositToken;
|
||||||
mapping(address account => mapping(address token => uint256 amount)) public override depositBalances;
|
|
||||||
mapping(address token => uint256 amount) public totalDepositSupply;
|
mapping(address token => uint256 amount) public totalDepositSupply;
|
||||||
|
|
||||||
uint256 public cumulativeRewardPerToken;
|
|
||||||
mapping(address account => uint256 amount) public override stakedAmounts;
|
mapping(address account => uint256 amount) public override stakedAmounts;
|
||||||
mapping(address account => uint256 amount) public claimableReward;
|
mapping(address account => uint256 amount) public claimableReward;
|
||||||
mapping(address account => uint256 amount) public previousCumulatedRewardPerToken;
|
|
||||||
mapping(address account => uint256 amount) public override cumulativeRewards;
|
mapping(address account => uint256 amount) public override cumulativeRewards;
|
||||||
mapping(address account => uint256 amount) public override averageStakedAmounts;
|
|
||||||
|
|
||||||
bool public inPrivateTransferMode;
|
mapping(address account => uint256 time) public lastUpdateTimes;
|
||||||
|
|
||||||
|
uint256 public tokensPerInterval;
|
||||||
|
address public rewardToken;
|
||||||
|
uint8 public decimals = 18;
|
||||||
|
|
||||||
bool public inPrivateStakingMode;
|
bool public inPrivateStakingMode;
|
||||||
bool public inPrivateClaimingMode;
|
bool public inPrivateClaimingMode;
|
||||||
mapping(address handler => bool status) public isHandler;
|
mapping(address handler => bool status) public isHandler;
|
||||||
|
|
||||||
event Claim(address receiver, uint256 amount);
|
event Claim(address receiver, uint256 amount);
|
||||||
|
event TokensPerIntervalChange(uint256 amount);
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol) {
|
constructor(address _rewardToken, address _depositToken, uint256 _tokensPerInterval, uint8 _decimals) {
|
||||||
name = _name;
|
rewardToken = _rewardToken;
|
||||||
symbol = _symbol;
|
isDepositToken[_depositToken] = true;
|
||||||
}
|
tokensPerInterval = _tokensPerInterval;
|
||||||
|
decimals = _decimals;
|
||||||
function initialize(address[] memory _depositTokens, address _distributor) external onlyGov {
|
|
||||||
require(!isInitialized, "RewardTracker: already initialized");
|
|
||||||
isInitialized = true;
|
|
||||||
|
|
||||||
for (uint256 i = 0; i < _depositTokens.length; i++) {
|
|
||||||
address depositToken = _depositTokens[i];
|
|
||||||
isDepositToken[depositToken] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
distributor = _distributor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDepositToken(address _depositToken, bool _isDepositToken) external onlyGov {
|
function setDepositToken(address _depositToken, bool _isDepositToken) external onlyGov {
|
||||||
isDepositToken[_depositToken] = _isDepositToken;
|
isDepositToken[_depositToken] = _isDepositToken;
|
||||||
}
|
}
|
||||||
|
//TODO:: 是否更新用户未领取的奖励
|
||||||
function setInPrivateTransferMode(bool _inPrivateTransferMode) external onlyGov {
|
function setTokensPerInterval(uint256 _amount) external onlyGov {
|
||||||
inPrivateTransferMode = _inPrivateTransferMode;
|
tokensPerInterval = _amount;
|
||||||
|
emit TokensPerIntervalChange(_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInPrivateStakingMode(bool _inPrivateStakingMode) external onlyGov {
|
function setInPrivateStakingMode(bool _inPrivateStakingMode) external onlyGov {
|
||||||
@ -85,10 +64,6 @@ contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable {
|
|||||||
IERC20(_token).safeTransfer(_account, _amount);
|
IERC20(_token).safeTransfer(_account, _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function balanceOf(address _account) external view override returns (uint256) {
|
|
||||||
return balances[_account];
|
|
||||||
}
|
|
||||||
|
|
||||||
function stake(address _depositToken, uint256 _amount) external override nonReentrant {
|
function stake(address _depositToken, uint256 _amount) external override nonReentrant {
|
||||||
if (inPrivateStakingMode) {
|
if (inPrivateStakingMode) {
|
||||||
revert("RewardTracker: action not enabled");
|
revert("RewardTracker: action not enabled");
|
||||||
@ -123,37 +98,6 @@ contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable {
|
|||||||
_unstake(_account, _depositToken, _amount, _receiver);
|
_unstake(_account, _depositToken, _amount, _receiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
function transfer(address _recipient, uint256 _amount) external override returns (bool) {
|
|
||||||
_transfer(msg.sender, _recipient, _amount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function approve(address _spender, uint256 _amount) external override returns (bool) {
|
|
||||||
_approve(msg.sender, _spender, _amount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function transferFrom(address _sender, address _recipient, uint256 _amount) external override returns (bool) {
|
|
||||||
if (isHandler[msg.sender]) {
|
|
||||||
_transfer(_sender, _recipient, _amount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
require(allowance[_sender][msg.sender] >= _amount, "RewardTracker: transfer amount exceeds allowance");
|
|
||||||
uint256 nextAllowance = allowance[_sender][msg.sender] - _amount;
|
|
||||||
_approve(_sender, msg.sender, nextAllowance);
|
|
||||||
_transfer(_sender, _recipient, _amount);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokensPerInterval() external view override returns (uint256) {
|
|
||||||
return IRewardDistributor(distributor).tokensPerInterval();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRewards() external override nonReentrant {
|
|
||||||
_updateRewards(address(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
function claim(address _receiver) external override nonReentrant returns (uint256) {
|
function claim(address _receiver) external override nonReentrant returns (uint256) {
|
||||||
if (inPrivateClaimingMode) {
|
if (inPrivateClaimingMode) {
|
||||||
revert("RewardTracker: action not enabled");
|
revert("RewardTracker: action not enabled");
|
||||||
@ -171,73 +115,21 @@ contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable {
|
|||||||
if (stakedAmount == 0) {
|
if (stakedAmount == 0) {
|
||||||
return claimableReward[_account];
|
return claimableReward[_account];
|
||||||
}
|
}
|
||||||
uint256 pendingRewards = IRewardDistributor(distributor).pendingRewards() * PRECISION;
|
uint256 pendingRewards = _getNextClaimableAmount(_account);
|
||||||
uint256 nextCumulativeRewardPerToken = cumulativeRewardPerToken + pendingRewards;
|
return claimableReward[_account] + pendingRewards;
|
||||||
return
|
|
||||||
claimableReward[_account] +
|
|
||||||
(stakedAmount / (10**decimals) * (nextCumulativeRewardPerToken - previousCumulatedRewardPerToken[_account])) /
|
|
||||||
PRECISION;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rewardToken() public view returns (address) {
|
|
||||||
return IRewardDistributor(distributor).rewardToken();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _claim(address _account, address _receiver) private returns (uint256) {
|
function _claim(address _account, address _receiver) private returns (uint256) {
|
||||||
_updateRewards(_account);
|
_updateRewards(_account);
|
||||||
|
|
||||||
uint256 tokenAmount = claimableReward[_account];
|
uint256 tokenAmount = claimableReward[_account];
|
||||||
claimableReward[_account] = 0;
|
claimableReward[_account] = 0;
|
||||||
|
|
||||||
if (tokenAmount > 0) {
|
if (tokenAmount > 0) {
|
||||||
IERC20(rewardToken()).safeTransfer(_receiver, tokenAmount);
|
IERC20(rewardToken).safeTransfer(_receiver, tokenAmount);
|
||||||
emit Claim(_account, tokenAmount);
|
emit Claim(_account, tokenAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenAmount;
|
return tokenAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mint(address _account, uint256 _amount) internal {
|
|
||||||
require(_account != address(0), "RewardTracker: mint to the zero address");
|
|
||||||
|
|
||||||
totalSupply = totalSupply + _amount;
|
|
||||||
balances[_account] = balances[_account] + _amount;
|
|
||||||
|
|
||||||
emit Transfer(address(0), _account, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _burn(address _account, uint256 _amount) internal {
|
|
||||||
require(_account != address(0), "RewardTracker: burn from the zero address");
|
|
||||||
require(balances[_account] >= _amount, "RewardTracker: burn amount exceeds balance");
|
|
||||||
balances[_account] = balances[_account] - _amount;
|
|
||||||
totalSupply = totalSupply - _amount;
|
|
||||||
|
|
||||||
emit Transfer(_account, address(0), _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _transfer(address _sender, address _recipient, uint256 _amount) private {
|
|
||||||
require(_sender != address(0), "RewardTracker: transfer from the zero address");
|
|
||||||
require(_recipient != address(0), "RewardTracker: transfer to the zero address");
|
|
||||||
|
|
||||||
if (inPrivateTransferMode) {
|
|
||||||
_validateHandler();
|
|
||||||
}
|
|
||||||
require(balances[_sender] >= _amount, "RewardTracker: transfer amount exceeds balance");
|
|
||||||
balances[_sender] = balances[_sender] - _amount;
|
|
||||||
balances[_recipient] = balances[_recipient] + _amount;
|
|
||||||
|
|
||||||
emit Transfer(_sender, _recipient, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _approve(address _owner, address _spender, uint256 _amount) private {
|
|
||||||
require(_owner != address(0), "RewardTracker: approve from the zero address");
|
|
||||||
require(_spender != address(0), "RewardTracker: approve to the zero address");
|
|
||||||
|
|
||||||
allowance[_owner][_spender] = _amount;
|
|
||||||
|
|
||||||
emit Approval(_owner, _spender, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _validateHandler() private view {
|
function _validateHandler() private view {
|
||||||
require(isHandler[msg.sender], "RewardTracker: forbidden");
|
require(isHandler[msg.sender], "RewardTracker: forbidden");
|
||||||
}
|
}
|
||||||
@ -245,75 +137,45 @@ contract RewardTracker is IERC20, ReentrancyGuard, IRewardTracker, Governable {
|
|||||||
function _stake(address _fundingAccount, address _account, address _depositToken, uint256 _amount) private {
|
function _stake(address _fundingAccount, address _account, address _depositToken, uint256 _amount) private {
|
||||||
require(_amount > 0, "RewardTracker: invalid _amount");
|
require(_amount > 0, "RewardTracker: invalid _amount");
|
||||||
require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken");
|
require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken");
|
||||||
|
|
||||||
IERC20(_depositToken).safeTransferFrom(_fundingAccount, address(this), _amount);
|
IERC20(_depositToken).safeTransferFrom(_fundingAccount, address(this), _amount);
|
||||||
|
|
||||||
_updateRewards(_account);
|
_updateRewards(_account);
|
||||||
|
|
||||||
stakedAmounts[_account] = stakedAmounts[_account] + _amount;
|
stakedAmounts[_account] = stakedAmounts[_account] + _amount;
|
||||||
depositBalances[_account][_depositToken] = depositBalances[_account][_depositToken] + _amount;
|
|
||||||
totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken] + _amount;
|
totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken] + _amount;
|
||||||
|
|
||||||
_mint(_account, _amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function _unstake(address _account, address _depositToken, uint256 _amount, address _receiver) private {
|
function _unstake(address _account, address _depositToken, uint256 _amount, address _receiver) private {
|
||||||
require(_amount > 0, "RewardTracker: invalid _amount");
|
require(_amount > 0, "RewardTracker: invalid _amount");
|
||||||
require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken");
|
require(isDepositToken[_depositToken], "RewardTracker: invalid _depositToken");
|
||||||
|
|
||||||
_updateRewards(_account);
|
_updateRewards(_account);
|
||||||
|
|
||||||
uint256 stakedAmount = stakedAmounts[_account];
|
uint256 stakedAmount = stakedAmounts[_account];
|
||||||
require(stakedAmounts[_account] >= _amount, "RewardTracker: _amount exceeds stakedAmount");
|
require(stakedAmount >= _amount, "RewardTracker: _amount exceeds stakedAmount");
|
||||||
|
|
||||||
stakedAmounts[_account] = stakedAmount - _amount;
|
stakedAmounts[_account] = stakedAmount - _amount;
|
||||||
|
|
||||||
uint256 depositBalance = depositBalances[_account][_depositToken];
|
|
||||||
require(depositBalance >= _amount, "RewardTracker: _amount exceeds depositBalance");
|
|
||||||
depositBalances[_account][_depositToken] = depositBalance - _amount;
|
|
||||||
totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken] - _amount;
|
totalDepositSupply[_depositToken] = totalDepositSupply[_depositToken] - _amount;
|
||||||
|
|
||||||
_burn(_account, _amount);
|
|
||||||
IERC20(_depositToken).safeTransfer(_receiver, _amount);
|
IERC20(_depositToken).safeTransfer(_receiver, _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateRewards(address _account) public {
|
||||||
|
_updateRewards(_account);
|
||||||
|
}
|
||||||
|
|
||||||
function _updateRewards(address _account) private {
|
function _updateRewards(address _account) private {
|
||||||
uint256 supply = totalSupply;
|
uint256 accountReward = _getNextClaimableAmount(_account);
|
||||||
uint256 blockReward = IRewardDistributor(distributor).distribute(supply, decimals);
|
lastUpdateTimes[_account] = block.timestamp;
|
||||||
|
|
||||||
|
|
||||||
uint256 _cumulativeRewardPerToken = cumulativeRewardPerToken;
|
|
||||||
if (supply > 0 && blockReward > 0) {
|
|
||||||
_cumulativeRewardPerToken = _cumulativeRewardPerToken + blockReward * PRECISION;
|
|
||||||
cumulativeRewardPerToken = _cumulativeRewardPerToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cumulativeRewardPerToken can only increase
|
|
||||||
// so if cumulativeRewardPerToken is zero, it means there are no rewards yet
|
|
||||||
if (_cumulativeRewardPerToken == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_account != address(0)) {
|
|
||||||
uint256 stakedAmount = stakedAmounts[_account];
|
|
||||||
uint256 accountReward = (stakedAmount / (10**decimals) * (_cumulativeRewardPerToken - previousCumulatedRewardPerToken[_account])) /
|
|
||||||
PRECISION;
|
|
||||||
uint256 _claimableReward = claimableReward[_account] + accountReward;
|
uint256 _claimableReward = claimableReward[_account] + accountReward;
|
||||||
|
|
||||||
claimableReward[_account] = _claimableReward;
|
claimableReward[_account] = _claimableReward;
|
||||||
previousCumulatedRewardPerToken[_account] = _cumulativeRewardPerToken;
|
|
||||||
|
|
||||||
if (_claimableReward > 0 && stakedAmounts[_account] > 0) {
|
if (_claimableReward > 0 && stakedAmounts[_account] > 0) {
|
||||||
uint256 nextCumulativeReward = cumulativeRewards[_account] + accountReward;
|
uint256 nextCumulativeReward = cumulativeRewards[_account] + accountReward;
|
||||||
|
|
||||||
averageStakedAmounts[_account] =
|
|
||||||
(averageStakedAmounts[_account] * cumulativeRewards[_account]) /
|
|
||||||
nextCumulativeReward +
|
|
||||||
(stakedAmount / (10**decimals) * accountReward) /
|
|
||||||
nextCumulativeReward;
|
|
||||||
|
|
||||||
cumulativeRewards[_account] = nextCumulativeReward;
|
cumulativeRewards[_account] = nextCumulativeReward;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getNextClaimableAmount(address _account) private view returns (uint256) {
|
||||||
|
uint256 timeDiff = block.timestamp - lastUpdateTimes[_account];
|
||||||
|
uint256 stakedAmount = stakedAmounts[_account];
|
||||||
|
if (stakedAmount == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint256 accountReward = (stakedAmount / (10 ** decimals)) * tokensPerInterval * timeDiff;
|
||||||
|
return accountReward;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,16 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
uint8 public decimals = 18;
|
uint8 public decimals = 18;
|
||||||
uint256 public vestingDuration;
|
uint256 public vestingDuration;
|
||||||
address public esToken;
|
address public esToken;
|
||||||
address public pairToken;
|
|
||||||
address public claimableToken;
|
address public claimableToken;
|
||||||
|
|
||||||
address public override rewardTracker;
|
address public override rewardTrackerCEC;
|
||||||
|
address public override rewardTrackerEsCEC;
|
||||||
|
|
||||||
uint256 public override totalSupply;
|
uint256 public override totalSupply;
|
||||||
uint256 public pairSupply;
|
uint256 public pairSupply;
|
||||||
bool public needCheckStake;
|
bool public needCheckStake;
|
||||||
|
|
||||||
mapping(address account => uint256 amount) public balances;
|
mapping(address account => uint256 amount) public balances;
|
||||||
mapping(address account => uint256 amount) public override pairAmounts;
|
|
||||||
mapping(address account => uint256 amount) public override cumulativeClaimAmounts;
|
mapping(address account => uint256 amount) public override cumulativeClaimAmounts;
|
||||||
mapping(address account => uint256 amount) public override claimedAmounts;
|
mapping(address account => uint256 amount) public override claimedAmounts;
|
||||||
mapping(address account => uint256 time) public lastVestingTimes;
|
mapping(address account => uint256 time) public lastVestingTimes;
|
||||||
@ -40,7 +39,6 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
event Claim(address indexed receiver, uint256 amount);
|
event Claim(address indexed receiver, uint256 amount);
|
||||||
event Deposit(address indexed account, uint256 amount);
|
event Deposit(address indexed account, uint256 amount);
|
||||||
event Withdraw(address indexed account, uint256 claimedAmount, uint256 balance);
|
event Withdraw(address indexed account, uint256 claimedAmount, uint256 balance);
|
||||||
event PairTransfer(address indexed from, address indexed to, uint256 value);
|
|
||||||
event DurationUpdated(uint256 duration);
|
event DurationUpdated(uint256 duration);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -48,18 +46,18 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
string memory _symbol,
|
string memory _symbol,
|
||||||
uint256 _vestingDuration,
|
uint256 _vestingDuration,
|
||||||
address _esToken,
|
address _esToken,
|
||||||
address _pairToken,
|
|
||||||
address _claimableToken,
|
address _claimableToken,
|
||||||
address _rewardTracker,
|
address _rewardTrackerCEC,
|
||||||
|
address _rewardTrackerEsCEC,
|
||||||
bool _needCheckStake
|
bool _needCheckStake
|
||||||
) {
|
) {
|
||||||
name = _name;
|
name = _name;
|
||||||
symbol = _symbol;
|
symbol = _symbol;
|
||||||
vestingDuration = _vestingDuration;
|
vestingDuration = _vestingDuration;
|
||||||
esToken = _esToken;
|
esToken = _esToken;
|
||||||
pairToken = _pairToken;
|
|
||||||
claimableToken = _claimableToken;
|
claimableToken = _claimableToken;
|
||||||
rewardTracker = _rewardTracker;
|
rewardTrackerCEC = _rewardTrackerCEC;
|
||||||
|
rewardTrackerEsCEC = _rewardTrackerEsCEC;
|
||||||
needCheckStake = _needCheckStake;
|
needCheckStake = _needCheckStake;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,12 +98,6 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
uint256 totalVested = balance + claimedAmount;
|
uint256 totalVested = balance + claimedAmount;
|
||||||
require(totalVested > 0, "Vester: vested amount is zero");
|
require(totalVested > 0, "Vester: vested amount is zero");
|
||||||
|
|
||||||
if (hasPairToken()) {
|
|
||||||
uint256 pairAmount = pairAmounts[account];
|
|
||||||
_burnPair(account, pairAmount);
|
|
||||||
IERC20(pairToken).safeTransfer(_receiver, pairAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
IERC20(esToken).safeTransfer(_receiver, balance);
|
IERC20(esToken).safeTransfer(_receiver, balance);
|
||||||
_burn(account, balance);
|
_burn(account, balance);
|
||||||
|
|
||||||
@ -116,8 +108,12 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
emit Withdraw(account, claimedAmount, balance);
|
emit Withdraw(account, claimedAmount, balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRewardTracker(address _rewardTracker) external onlyGov {
|
function setRewardTrackerCEC(address _rewardTracker) external onlyGov {
|
||||||
rewardTracker = _rewardTracker;
|
rewardTrackerCEC = _rewardTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRewardTrackerEsCEC(address _rewardTracker) external onlyGov {
|
||||||
|
rewardTrackerEsCEC = _rewardTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDuration(uint256 _vestingDuration) external onlyGov {
|
function updateDuration(uint256 _vestingDuration) external onlyGov {
|
||||||
@ -138,9 +134,14 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
function getMaxVestableAmount(address _account) public view override returns (uint256) {
|
function getMaxVestableAmount(address _account) public view override returns (uint256) {
|
||||||
uint256 maxVestableAmount = bonusRewards[_account];
|
uint256 maxVestableAmount = bonusRewards[_account];
|
||||||
|
|
||||||
if (hasRewardTracker()) {
|
if (hasRewardTrackerCEC()) {
|
||||||
uint256 cumulativeReward = IRewardTracker(rewardTracker).cumulativeRewards(_account);
|
uint256 cumulativeRewardCEC = IRewardTracker(rewardTrackerCEC).cumulativeRewards(_account);
|
||||||
maxVestableAmount = maxVestableAmount + cumulativeReward;
|
maxVestableAmount = maxVestableAmount + cumulativeRewardCEC ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRewardTrackerEsCEC()) {
|
||||||
|
uint256 cumulativeRewardEsCEC = IRewardTracker(rewardTrackerEsCEC).cumulativeRewards(_account);
|
||||||
|
maxVestableAmount = maxVestableAmount + cumulativeRewardEsCEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 cumulativeRewardDeduction = cumulativeRewardDeductions[_account];
|
uint256 cumulativeRewardDeduction = cumulativeRewardDeductions[_account];
|
||||||
@ -152,43 +153,12 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
return maxVestableAmount - cumulativeRewardDeduction;
|
return maxVestableAmount - cumulativeRewardDeduction;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCombinedAverageStakedAmount(address _account) public view override returns (uint256) {
|
function hasRewardTrackerCEC() public view returns (bool) {
|
||||||
if (!hasRewardTracker()) {
|
return rewardTrackerCEC != address(0);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 cumulativeReward = IRewardTracker(rewardTracker).cumulativeRewards(_account);
|
function hasRewardTrackerEsCEC() public view returns (bool) {
|
||||||
if (cumulativeReward == 0) {
|
return rewardTrackerEsCEC != address(0);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IRewardTracker(rewardTracker).averageStakedAmounts(_account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPairAmount(address _account, uint256 _esAmount) public view returns (uint256) {
|
|
||||||
if (!hasRewardTracker()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 combinedAverageStakedAmount = getCombinedAverageStakedAmount(_account);
|
|
||||||
if (combinedAverageStakedAmount == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 maxVestableAmount = getMaxVestableAmount(_account);
|
|
||||||
if (maxVestableAmount == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (_esAmount * combinedAverageStakedAmount) / maxVestableAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasRewardTracker() public view returns (bool) {
|
|
||||||
return rewardTracker != address(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasPairToken() public view returns (bool) {
|
|
||||||
return pairToken != address(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTotalVested(address _account) public view returns (uint256) {
|
function getTotalVested(address _account) public view returns (uint256) {
|
||||||
@ -238,15 +208,6 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
emit Transfer(address(0), _account, _amount);
|
emit Transfer(address(0), _account, _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mintPair(address _account, uint256 _amount) private {
|
|
||||||
require(_account != address(0), "Vester: mint to the zero address");
|
|
||||||
|
|
||||||
pairSupply = pairSupply + _amount;
|
|
||||||
pairAmounts[_account] = pairAmounts[_account] + _amount;
|
|
||||||
|
|
||||||
emit PairTransfer(address(0), _account, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _burn(address _account, uint256 _amount) private {
|
function _burn(address _account, uint256 _amount) private {
|
||||||
require(_account != address(0), "Vester: burn from the zero address");
|
require(_account != address(0), "Vester: burn from the zero address");
|
||||||
require(balances[_account] >= _amount, "Vester: balance is not enough");
|
require(balances[_account] >= _amount, "Vester: balance is not enough");
|
||||||
@ -256,14 +217,6 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
emit Transfer(_account, address(0), _amount);
|
emit Transfer(_account, address(0), _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _burnPair(address _account, uint256 _amount) private {
|
|
||||||
require(_account != address(0), "Vester: burn from the zero address");
|
|
||||||
require(pairAmounts[_account] >= _amount, "Vester: balance is not enough");
|
|
||||||
pairAmounts[_account] = pairAmounts[_account] - _amount;
|
|
||||||
pairSupply = pairSupply - _amount;
|
|
||||||
|
|
||||||
emit PairTransfer(_account, address(0), _amount);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @dev Deposit ES tokens to the contract
|
* @dev Deposit ES tokens to the contract
|
||||||
*/
|
*/
|
||||||
@ -275,18 +228,9 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
|
|
||||||
_mint(_account, _amount);
|
_mint(_account, _amount);
|
||||||
|
|
||||||
if (hasPairToken()) {
|
if (needCheckStake && hasRewardTrackerCEC()) {
|
||||||
uint256 pairAmount = pairAmounts[_account];
|
|
||||||
uint256 nextPairAmount = getPairAmount(_account, balances[_account]);
|
|
||||||
if (nextPairAmount > pairAmount) {
|
|
||||||
uint256 pairAmountDiff = nextPairAmount - pairAmount;
|
|
||||||
IERC20(pairToken).safeTransferFrom(_account, address(this), pairAmountDiff);
|
|
||||||
_mintPair(_account, pairAmountDiff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (needCheckStake && hasRewardTracker()) {
|
|
||||||
// if u want to transfer 100 esCec to cec, u need to have 100 cec in stake
|
// if u want to transfer 100 esCec to cec, u need to have 100 cec in stake
|
||||||
uint256 cecAmount = IRewardTracker(rewardTracker).depositBalances(_account, claimableToken);
|
uint256 cecAmount = IRewardTracker(rewardTrackerCEC).stakedAmounts(_account);
|
||||||
require(balances[_account] <= cecAmount, "Vester: insufficient cec balance");
|
require(balances[_account] <= cecAmount, "Vester: insufficient cec balance");
|
||||||
}
|
}
|
||||||
uint256 maxAmount = getMaxVestableAmount(_account);
|
uint256 maxAmount = getMaxVestableAmount(_account);
|
||||||
@ -302,11 +246,9 @@ contract Vester is IVester, IERC20, ReentrancyGuard, Governable {
|
|||||||
function _updateVesting(address _account) public {
|
function _updateVesting(address _account) public {
|
||||||
uint256 amount = _getNextClaimableAmount(_account);
|
uint256 amount = _getNextClaimableAmount(_account);
|
||||||
lastVestingTimes[_account] = block.timestamp;
|
lastVestingTimes[_account] = block.timestamp;
|
||||||
|
|
||||||
if (amount == 0) {
|
if (amount == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// transfer claimableAmount from balances to cumulativeClaimAmounts
|
// transfer claimableAmount from balances to cumulativeClaimAmounts
|
||||||
_burn(_account, amount);
|
_burn(_account, amount);
|
||||||
cumulativeClaimAmounts[_account] = cumulativeClaimAmounts[_account] + amount;
|
cumulativeClaimAmounts[_account] = cumulativeClaimAmounts[_account] + amount;
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity 0.8.19;
|
|
||||||
|
|
||||||
interface IRewardDistributor {
|
|
||||||
function rewardToken() external view returns (address);
|
|
||||||
function tokensPerInterval() external view returns (uint256);
|
|
||||||
function pendingRewards() external view returns (uint256);
|
|
||||||
function distribute(uint256 _amount, uint256 _decimals) external returns (uint256);
|
|
||||||
}
|
|
@ -2,17 +2,15 @@
|
|||||||
pragma solidity 0.8.19;
|
pragma solidity 0.8.19;
|
||||||
|
|
||||||
interface IRewardTracker {
|
interface IRewardTracker {
|
||||||
function depositBalances(address _account, address _depositToken) external view returns (uint256);
|
|
||||||
function stakedAmounts(address _account) external view returns (uint256);
|
function stakedAmounts(address _account) external view returns (uint256);
|
||||||
function updateRewards() external;
|
function updateRewards(address _account) external;
|
||||||
function stake(address _depositToken, uint256 _amount) external;
|
function stake(address _depositToken, uint256 _amount) external;
|
||||||
function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount) external;
|
function stakeForAccount(address _fundingAccount, address _account, address _depositToken, uint256 _amount) external;
|
||||||
function unstake(address _depositToken, uint256 _amount) external;
|
function unstake(address _depositToken, uint256 _amount) external;
|
||||||
function unstakeForAccount(address _account, address _depositToken, uint256 _amount, address _receiver) external;
|
function unstakeForAccount(address _account, address _depositToken, uint256 _amount, address _receiver) external;
|
||||||
function tokensPerInterval() external view returns (uint256);
|
|
||||||
function claim(address _receiver) external returns (uint256);
|
function claim(address _receiver) external returns (uint256);
|
||||||
function claimForAccount(address _account, address _receiver) external returns (uint256);
|
function claimForAccount(address _account, address _receiver) external returns (uint256);
|
||||||
function claimable(address _account) external view returns (uint256);
|
function claimable(address _account) external view returns (uint256);
|
||||||
function averageStakedAmounts(address _account) external view returns (uint256);
|
// function averageStakedAmounts(address _account) external view returns (uint256);
|
||||||
function cumulativeRewards(address _account) external view returns (uint256);
|
function cumulativeRewards(address _account) external view returns (uint256);
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ interface IVester {
|
|||||||
function needCheckStake() external view returns (bool);
|
function needCheckStake() external view returns (bool);
|
||||||
function updateVesting(address _account) external;
|
function updateVesting(address _account) external;
|
||||||
|
|
||||||
function rewardTracker() external view returns (address);
|
function rewardTrackerCEC() external view returns (address);
|
||||||
|
function rewardTrackerEsCEC() external view returns (address);
|
||||||
|
|
||||||
function claimForAccount(address _account, address _receiver) external returns (uint256);
|
function claimForAccount(address _account, address _receiver) external returns (uint256);
|
||||||
|
|
||||||
function claimable(address _account) external view returns (uint256);
|
function claimable(address _account) external view returns (uint256);
|
||||||
function cumulativeClaimAmounts(address _account) external view returns (uint256);
|
function cumulativeClaimAmounts(address _account) external view returns (uint256);
|
||||||
function claimedAmounts(address _account) external view returns (uint256);
|
function claimedAmounts(address _account) external view returns (uint256);
|
||||||
function pairAmounts(address _account) external view returns (uint256);
|
|
||||||
function getVestedAmount(address _account) external view returns (uint256);
|
function getVestedAmount(address _account) external view returns (uint256);
|
||||||
function cumulativeRewardDeductions(address _account) external view returns (uint256);
|
function cumulativeRewardDeductions(address _account) external view returns (uint256);
|
||||||
function bonusRewards(address _account) external view returns (uint256);
|
function bonusRewards(address _account) external view returns (uint256);
|
||||||
@ -21,5 +21,4 @@ interface IVester {
|
|||||||
function setBonusRewards(address _account, uint256 _amount) external;
|
function setBonusRewards(address _account, uint256 _amount) external;
|
||||||
|
|
||||||
function getMaxVestableAmount(address _account) external view returns (uint256);
|
function getMaxVestableAmount(address _account) external view returns (uint256);
|
||||||
function getCombinedAverageStakedAmount(address _account) external view returns (uint256);
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user