TombBasedRewardPools
76 removals
274 lines
100 additions
299 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
// Note that this pool has no minter key of tSHARE (rewards).
// Note that this pool has no minter key of bSHARE (rewards).
// Instead, the governance will call tSHARE distributeReward method and send reward to this pool at the beginning.
// Instead, the governance will call bSHARE distributeReward method and send reward to this pool at the beginning.
contract TShareRewardPool {
contract BShareRewardPool {
using SafeMath for uint256;
using SafeMath for uint256;
using SafeERC20 for IERC20;
using SafeERC20 for IERC20;
// governance
// governance
address public operator;
address public operator;
// Info of each user.
// Info of each user.
struct UserInfo {
struct UserInfo {
uint256 amount; // How many LP tokens the user has provided.
uint256 amount; // How many LP tokens the user has provided.
uint256 rewardDebt; // Reward debt. See explanation below.
uint256 rewardDebt; // Reward debt. See explanation below.
}
}
// Info of each pool.
// Info of each pool.
struct PoolInfo {
struct PoolInfo {
IERC20 token; // Address of LP token contract.
IERC20 token; // Address of LP token contract.
uint256 allocPoint; // How many allocation points assigned to this pool. tSHAREs to distribute per block.
uint256 allocPoint; // How many allocation points assigned to this pool. bSHAREs to distribute per block.
uint256 lastRewardTime; // Last time that tSHAREs distribution occurs.
uint256 lastRewardTime; // Last time that bSHAREs distribution occurs.
uint256 accTSharePerShare; // Accumulated tSHAREs per share, times 1e18. See below.
uint256 accBSharePerShare; // Accumulated bSHAREs per share, times 1e18. See below.
bool isStarted; // if lastRewardTime has passed
bool isStarted; // if lastRewardTime has passed
}
}
IERC20 public tshare;
IERC20 public bshare;
// Info of each pool.
// Info of each pool.
PoolInfo[] public poolInfo;
PoolInfo[] public poolInfo;
// Info of each user that stakes LP tokens.
// Info of each user that stakes LP tokens.
mapping(uint256 => mapping(address => UserInfo)) public userInfo;
mapping(uint256 => mapping(address => UserInfo)) public userInfo;
// Total allocation points. Must be the sum of all allocation points in all pools.
// Total allocation points. Must be the sum of all allocation points in all pools.
uint256 public totalAllocPoint = 0;
uint256 public totalAllocPoint = 0;
// The time when tSHARE mining starts.
// The time when bSHARE mining starts.
uint256 public poolStartTime;
uint256 public poolStartTime;
// The time when tSHARE mining ends.
// The time when bSHARE mining ends.
uint256 public poolEndTime;
uint256 public poolEndTime;
uint256 public tSharePerSecond = 0.00186122 ether; // 59500 tshare / (370 days * 24h * 60min * 60s)
address public daoFundAddress;
uint256 public runningTime = 370 days; // 370 days
uint256 public constant TOTAL_REWARDS = 59500 ether;
uint256 public bSharePerSecond = 0.003486 ether; // 50000 bshare / (166 days * 24h * 60min * 60s)
uint256 public runningTime = 166 days; // 166 days
uint256 public constant TOTAL_REWARDS = 50000 ether;
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
event RewardPaid(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 amount);
constructor(
constructor(
address _tshare,
address _bshare,
address _daoFund,
uint256 _poolStartTime
uint256 _poolStartTime
) public {
) {
require(block.timestamp < _poolStartTime, "late");
require(block.timestamp < _poolStartTime, "pool cant be started in the past");
if (_tshare != address(0)) tshare = IERC20(_tshare);
if (_bshare != address(0)) bshare = IERC20(_bshare);
if(_daoFund != address(0)) daoFundAddress = _daoFund;
poolStartTime = _poolStartTime;
poolStartTime = _poolStartTime;
poolEndTime = poolStartTime + runningTime;
poolEndTime = poolStartTime + runningTime;
operator = msg.sender;
operator = msg.sender;
}
}
modifier onlyOperator() {
modifier onlyOperator() {
require(operator == msg.sender, "TShareRewardPool: caller is not the operator");
require(operator == msg.sender, "BShareRewardPool: caller is not the operator");
_;
_;
}
}
function checkPoolDuplicate(IERC20 _token) internal view {
function checkPoolDuplicate(IERC20 _token) internal view {
uint256 length = poolInfo.length;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
for (uint256 pid = 0; pid < length; ++pid) {
require(poolInfo[pid].token != _token, "TShareRewardPool: existing pool?");
require(poolInfo[pid].token != _token, "BShareRewardPool: existing pool?");
}
}
}
}
// Add a new lp to the pool. Can only be called by the owner.
// Add new lp to the pool. Can only be called by operator.
function add(
function add(
uint256 _allocPoint,
uint256 _allocPoint,
IERC20 _token,
IERC20 _token,
bool _withUpdate,
bool _withUpdate,
uint256 _lastRewardTime
uint256 _lastRewardTime
) public onlyOperator {
) public onlyOperator {
checkPoolDuplicate(_token);
checkPoolDuplicate(_token);
if (_withUpdate) {
if (_withUpdate) {
massUpdatePools();
massUpdatePools();
}
}
if (block.timestamp < poolStartTime) {
if (block.timestamp < poolStartTime) {
// chef is sleeping
// chef is sleeping
if (_lastRewardTime == 0) {
if (_lastRewardTime == 0) {
_lastRewardTime = poolStartTime;
_lastRewardTime = poolStartTime;
} else {
} else {
if (_lastRewardTime < poolStartTime) {
if (_lastRewardTime < poolStartTime) {
_lastRewardTime = poolStartTime;
_lastRewardTime = poolStartTime;
}
}
}
}
} else {
} else {
// chef is cooking
// chef is cooking
if (_lastRewardTime == 0 || _lastRewardTime < block.timestamp) {
if (_lastRewardTime == 0 || _lastRewardTime < block.timestamp) {
_lastRewardTime = block.timestamp;
_lastRewardTime = block.timestamp;
}
}
}
}
bool _isStarted =
bool _isStarted = (_lastRewardTime <= poolStartTime) || (_lastRewardTime <= block.timestamp);
(_lastRewardTime <= poolStartTime) ||
(_lastRewardTime <= block.timestamp);
poolInfo.push(PoolInfo({
poolInfo.push(PoolInfo({
token : _token,
token: _token,
allocPoint : _allocPoint,
allocPoint: _allocPoint,
lastRewardTime : _lastRewardTime,
lastRewardTime: _lastRewardTime,
accTSharePerShare : 0,
accBSharePerShare: 0,
isStarted : _isStarted
isStarted: _isStarted
}));
}));
if (_isStarted) {
if (_isStarted) {
totalAllocPoint = totalAllocPoint.add(_allocPoint);
totalAllocPoint = totalAllocPoint.add(_allocPoint);
}
}
}
}
// Update the given pool's tSHARE allocation point. Can only be called by the owner.
// starting allocations for our pools
// BASED-TOMB LP: 21500 $BSHARE
// BSHARE-TOMB LP: 21000 $BSHARE
// TEAM: 4500 $BSHARE
// CURVE STABLES LP (OR GEIST STABLES LP): 3000 $BSHARE
// Update the given pool's bSHARE allocation point. Can only be called by the operator.
// @allocPoints for TEAM can NOT be altered after added - PID 2
// @allocPoints for main LP pools can NOT be smaller than 12,000
function set(uint256 _pid, uint256 _allocPoint) public onlyOperator {
function set(uint256 _pid, uint256 _allocPoint) public onlyOperator {
massUpdatePools();
massUpdatePools();
require (_pid != 2, "CAN NOT ADJUST TEAM ALLOCATIONS");
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
if (pool.isStarted) {
totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add(
if (_pid == 0 || _pid == 1) {
_allocPoint
require(_allocPoint >= 12000 * 10**18, "out of range"); // >= allocations for lp pools cant be less than 12,000
);
if (pool.isStarted) {
totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add(_allocPoint);
}
} else if (_pid > 2) {
if (pool.isStarted) {
totalAllocPoint = totalAllocPoint.sub(pool.allocPoint).add(_allocPoint);
}
}
}
pool.allocPoint = _allocPoint;
pool.allocPoint = _allocPoint;
}
}
// Return accumulate rewards over the given _from to _to block.
// Return accumulate rewards over the given _from to _to block.
function getGeneratedReward(uint256 _fromTime, uint256 _toTime) public view returns (uint256) {
function getGeneratedReward(uint256 _fromTime, uint256 _toTime) public view returns (uint256) {
if (_fromTime >= _toTime) return 0;
if (_fromTime >= _toTime) return 0;
if (_toTime >= poolEndTime) {
if (_toTime >= poolEndTime) {
if (_fromTime >= poolEndTime) return 0;
if (_fromTime >= poolEndTime) return 0;
if (_fromTime <= poolStartTime) return poolEndTime.sub(poolStartTime).mul(tSharePerSecond);
if (_fromTime <= poolStartTime) return poolEndTime.sub(poolStartTime).mul(bSharePerSecond);
return poolEndTime.sub(_fromTime).mul(tSharePerSecond);
return poolEndTime.sub(_fromTime).mul(bSharePerSecond);
} else {
} else {
if (_toTime <= poolStartTime) return 0;
if (_toTime <= poolStartTime) return 0;
if (_fromTime <= poolStartTime) return _toTime.sub(poolStartTime).mul(tSharePerSecond);
if (_fromTime <= poolStartTime) return _toTime.sub(poolStartTime).mul(bSharePerSecond);
return _toTime.sub(_fromTime).mul(tSharePerSecond);
return _toTime.sub(_fromTime).mul(bSharePerSecond);
}
}
}
}
// View function to see pending tSHAREs on frontend.
// View function to see pending bSHAREs on frontend.
function pendingShare(uint256 _pid, address _user) external view returns (uint256) {
function pendingShare(uint256 _pid, address _user) external view returns (uint256) {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_user];
UserInfo storage user = userInfo[_pid][_user];
uint256 accTSharePerShare = pool.accTSharePerShare;
uint256 accBSharePerShare = pool.accBSharePerShare;
uint256 tokenSupply = pool.token.balanceOf(address(this));
uint256 tokenSupply = pool.token.balanceOf(address(this));
if (block.timestamp > pool.lastRewardTime && tokenSupply != 0) {
if (block.timestamp > pool.lastRewardTime && tokenSupply != 0) {
uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp);
uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp);
uint256 _tshareReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint);
uint256 _bshareReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint);
accTSharePerShare = accTSharePerShare.add(_tshareReward.mul(1e18).div(tokenSupply));
accBSharePerShare = accBSharePerShare.add(_bshareReward.mul(1e18).div(tokenSupply));
}
}
return user.amount.mul(accTSharePerShare).div(1e18).sub(user.rewardDebt);
return user.amount.mul(accBSharePerShare).div(1e18).sub(user.rewardDebt);
}
}
// Update reward variables for all pools. Be careful of gas spending!
// Update reward variables for all pools. Be careful of gas spending!
function massUpdatePools() public {
function massUpdatePools() public {
uint256 length = poolInfo.length;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
for (uint256 pid = 0; pid < length; ++pid) {
updatePool(pid);
updatePool(pid);
}
}
}
}
// Update reward variables of the given pool to be up-to-date.
// Update reward variables of the given pool to be up-to-date.
function updatePool(uint256 _pid) public {
function updatePool(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
if (block.timestamp <= pool.lastRewardTime) {
if (block.timestamp <= pool.lastRewardTime) {
return;
return;
}
}
uint256 tokenSupply = pool.token.balanceOf(address(this));
uint256 tokenSupply = pool.token.balanceOf(address(this));
if (tokenSupply == 0) {
if (tokenSupply == 0) {
pool.lastRewardTime = block.timestamp;
pool.lastRewardTime = block.timestamp;
return;
return;
}
}
if (!pool.isStarted) {
if (!pool.isStarted) {
pool.isStarted = true;
pool.isStarted = true;
totalAllocPoint = totalAllocPoint.add(pool.allocPoint);
totalAllocPoint = totalAllocPoint.add(pool.allocPoint);
}
}
if (totalAllocPoint > 0) {
if (totalAllocPoint > 0) {
uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp);
uint256 _generatedReward = getGeneratedReward(pool.lastRewardTime, block.timestamp);
uint256 _tshareReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint);
uint256 _bshareReward = _generatedReward.mul(pool.allocPoint).div(totalAllocPoint);
pool.accTSharePerShare = pool.accTSharePerShare.add(_tshareReward.mul(1e18).div(tokenSupply));
pool.accBSharePerShare = pool.accBSharePerShare.add(_bshareReward.mul(1e18).div(tokenSupply));
}
}
pool.lastRewardTime = block.timestamp;
pool.lastRewardTime = block.timestamp;
}
}
// Deposit LP tokens.
// Deposit LP tokens.
function deposit(uint256 _pid, uint256 _amount) public {
function deposit(uint256 _pid, uint256 _amount) public {
address _sender = msg.sender;
address _sender = msg.sender;
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_sender];
UserInfo storage user = userInfo[_pid][_sender];
updatePool(_pid);
updatePool(_pid);
if (user.amount > 0) {
if (user.amount > 0) {
uint256 _pending = user.amount.mul(pool.accTSharePerShare).div(1e18).sub(user.rewardDebt);
uint256 _pending = user.amount.mul(pool.accBSharePerShare).div(1e18).sub(user.rewardDebt);
if (_pending > 0) {
if (_pending > 0) {
safeTShareTransfer(_sender, _pending);
safeBShareTransfer(_sender, _pending);
emit RewardPaid(_sender, _pending);
emit RewardPaid(_sender, _pending);
}
}
}
}
if (_amount > 0) {
if (_amount > 0 && _pid > 2) {
pool.token.safeTransferFrom(_sender, address(this), _amount);
uint256 depositDebt = _amount.mul(20).div(10000);
user.amount = user.amount.add(_amount.sub(depositDebt));
pool.token.safeTransfer(daoFundAddress, depositDebt);
} else if (_amount > 0 && _pid <= 2) {
pool.token.safeTransferFrom(_sender, address(this), _amount);
pool.token.safeTransferFrom(_sender, address(this), _amount);
user.amount = user.amount.add(_amount);
user.amount = user.amount.add(_amount);
}
}
user.rewardDebt = user.amount.mul(pool.accTSharePerShare).div(1e18);
user.rewardDebt = user.amount.mul(pool.accBSharePerShare).div(1e18);
emit Deposit(_sender, _pid, _amount);
emit Deposit(_sender, _pid, _amount);
}
}
// Withdraw LP tokens.
// Withdraw LP tokens.
function withdraw(uint256 _pid, uint256 _amount) public {
function withdraw(uint256 _pid, uint256 _amount) public {
address _sender = msg.sender;
address _sender = msg.sender;
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][_sender];
UserInfo storage user = userInfo[_pid][_sender];
require(user.amount >= _amount, "withdraw: not good");
require(user.amount >= _amount, "withdraw: not good");
updatePool(_pid);
updatePool(_pid);
uint256 _pending = user.amount.mul(pool.accTSharePerShare).div(1e18).sub(user.rewardDebt);
uint256 _pending = user.amount.mul(pool.accBSharePerShare).div(1e18).sub(user.rewardDebt);
if (_pending > 0) {
if (_pending > 0) {
safeTShareTransfer(_sender, _pending);
safeBShareTransfer(_sender, _pending);
emit RewardPaid(_sender, _pending);
emit RewardPaid(_sender, _pending);
}
}
if (_amount > 0) {
if (_amount > 0) {
user.amount = user.amount.sub(_amount);
user.amount = user.amount.sub(_amount);
pool.token.safeTransfer(_sender, _amount);
pool.token.safeTransfer(_sender, _amount);
}
}
user.rewardDebt = user.amount.mul(pool.accTSharePerShare).div(1e18);
user.rewardDebt = user.amount.mul(pool.accBSharePerShare).div(1e18);
emit Withdraw(_sender, _pid, _amount);
emit Withdraw(_sender, _pid, _amount);
}
}
// Withdraw without caring about rewards. EMERGENCY ONLY.
// Withdraw without caring about rewards. EMERGENCY ONLY.
function emergencyWithdraw(uint256 _pid) public {
function emergencyWithdraw(uint256 _pid) public {
PoolInfo storage pool = poolInfo[_pid];
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
UserInfo storage user = userInfo[_pid][msg.sender];
uint256 _amount = user.amount;
uint256 _amount = user.amount;
user.amount = 0;
user.amount = 0;
user.rewardDebt = 0;
user.rewardDebt = 0;
pool.token.safeTransfer(msg.sender, _amount);
pool.token.safeTransfer(msg.sender, _amount);
emit EmergencyWithdraw(msg.sender, _pid, _amount);
emit EmergencyWithdraw(msg.sender, _pid, _amount);
}
}
// Safe tshare transfer function, just in case if rounding error causes pool to not have enough tSHAREs.
// Safe bshare transfer function, just in case if rounding error causes pool to not have enough bSHAREs.
function safeTShareTransfer(address _to, uint256 _amount) internal {
function safeBShareTransfer(address _to, uint256 _amount) internal {
uint256 _tshareBal = tshare.balanceOf(address(this));
uint256 _bshareBal = bshare.balanceOf(address(this));
if (_tshareBal > 0) {
if (_bshareBal > 0) {
if (_amount > _tshareBal) {
if (_amount > _bshareBal) {
tshare.safeTransfer(_to, _tshareBal);
bshare.safeTransfer(_to, _bshareBal);
} else {
} else {
tshare.safeTransfer(_to, _amount);
bshare.safeTransfer(_to, _amount);
}
}
}
}
}
}
function setOperator(address _operator) external onlyOperator {
function setOperator(address _operator) external onlyOperator {
operator = _operator;
operator = _operator;
}
}
function governanceRecoverUnsupported(IERC20 _token, uint256 amount, address to) external onlyOperator {
function governanceRecoverUnsupported(IERC20 _token, uint256 amount, address to) external onlyOperator {
if (block.timestamp < poolEndTime + 90 days) {
if (block.timestamp < poolEndTime + 90 days) {
// do not allow to drain core token (tSHARE or lps) if less than 90 days after pool ends
// do not allow to drain core token (tSHARE or lps) if less than 90 days after pool ends
require(_token != tshare, "tshare");
require(_token != bshare, "bshare");
uint256 length = poolInfo.length;
uint256 length = poolInfo.length;
for (uint256 pid = 0; pid < length; ++pid) {
for (uint256 pid = 0; pid < length; ++pid) {
PoolInfo storage pool = poolInfo[pid];
PoolInfo storage pool = poolInfo[pid];
require(_token != pool.token, "pool.token");
require(_token != pool.token, "pool.token");
}
}
}
}
_token.safeTransfer(to, amount);
_token.safeTransfer(to, amount);
}
}
}
}