Staking.sol
The Staking smart contract is compiled with solidity v0.8.4 and uses the following libraries (and related dependencies):
Pausable.sol from OpenZeppelin
IERC20.sol from OpenZeppelin
SafeERC20.sol from OpenZeppelin
Context.sol from OpenZeppelin
UniswapV2Pair.sol from UniswapV2
PRBMathUD60x18.sol from PRBMath
The contract emits events for logging deposits and withdrawals.
computeCeShareFromLp
/**
* @dev Public View function to get ce share of LP deposit for a user.
* This intializes a UniswapV2Pair on the LP token address (which will be on univ2)
* It computes the CE share for an LP amount using the total supply of the LP token and then Reserve amounts for both tokens in the pair
* It ensures an accurate compute by checking reserve token addresses in the pair
* @param _pid the pool index
* @param _amount the amount of LP token being deposited
* @return amount of ce that corresponds to the lp deposit amount
*/
function computeCeShareFromLp(uint256 _pid, uint256 _amount) public view validatePoolByPid(_pid) returns (uint256) {
PoolInfo storage pool = poolInfo[_pid];
uint256 totalLpSupply = pool.lpToken.totalSupply();
IUniswapV2Pair pair = IUniswapV2Pair(address(pool.lpToken));
(uint112 res0, uint112 res1, ) = pair.getReserves();
IERC20 token0 = IERC20(pair.token0());
IERC20 token1 = IERC20(pair.token1());
uint256 ceShare;
if (address(token0) == address(CeToken)) {
ceShare = _amount.mul(res0).div(totalLpSupply);
} else if (address(token1) == address(CeToken)) {
ceShare = _amount.mul(res1).div(totalLpSupply);
} else {
ceShare = 0;
}
return ceShare;
}
computeReward
// coming soon
getApySecScaled
// coming soon
getApyMonScaled
// coming soon
addTimeLock
/**
* @dev Time lock for adding lp pool. Can only be called by the owner.
* This will set the locktime for an lp pool to 48 hours after current block timestamp
* @param _lpToken the lp token for the pool
*/
function addTimeLock(IERC20 _lpToken) public onlyOwner {
lockTime[address(_lpToken)] = block.timestamp + 172800;
}
add
/**
* @dev Add a new lp to the pool, reset the locktime for that pool. Can only be called by the owner.
* XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
* require statements to revert state when conditions are not met
* checks whether CeToken is in the _lpToken pair before being added as a pool
* checks whether locktime was initiated for the lp pool
* checks whether current block timestamp is greater than locktime for the lp pool
* @param _allocPoint pool allocation points (unused in other methods)
* @param _lpToken the lp token for the pool
* @param _pegToken the peg token for the pool
*/
function add(uint256 _allocPoint, IERC20 _lpToken, IERC20 _pegToken) public onlyOwner {
IUniswapV2Pair pair = IUniswapV2Pair(address(_lpToken));
IERC20 token0 = IERC20(pair.token0());
IERC20 token1 = IERC20(pair.token1());
require(address(token0) == address(CeToken) || address(token1) == address(CeToken), "add: CeToken not found in pair");
require(lockTime[address(_lpToken)] != 0, "add: timelock not initiated");
require(block.timestamp > lockTime[address(_lpToken)], "add: timelock not complete");
lockTime[address(_lpToken)] = 0;
uint256 lastRewardTime = block.timestamp > startTime ? block.timestamp : startTime;
totalAllocPoint = totalAllocPoint + _allocPoint;
poolInfo.push(PoolInfo({
lpToken: _lpToken,
allocPoint: _allocPoint,
lastRewardTime: lastRewardTime,
accRewardPerShare: 0,
pegToken: _pegToken
}));
}
deposit
/**
* @dev Deposit LP tokens to staking contract for CE allocation and emits an event.
* This will transfer any pending rewards to the user
* This will transfer the LP token amount to the contract
* This will transfer the peg token to the user based on CE share compute from LP token
* This will also adjust user information to reflect latest deposit amounts, deposit time, and reset withdrawal request time
* @param _pid the pool index
* @param _amount the amount of LP tokens being deposited
*/
function deposit(uint256 _pid, uint256 _amount) public validatePoolByPid(_pid) {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
if (user.amountCe > 0 && user.amount > 0) {
uint256 rewardAmount = computeReward(_pid, address(msg.sender));
safeCeTransfer(msg.sender, rewardAmount);
}
uint256 _amountCe = computeCeShareFromLp(_pid, _amount);
pool.lpToken.safeTransferFrom(address(msg.sender), address(this), _amount);
pool.pegToken.safeTransfer(address(msg.sender), _amountCe);
user.amount = user.amount + _amount;
user.amountCe = user.amountCe + _amountCe;
user.depositTime = block.timestamp;
user.withdrawRequest = 0;
emit Deposit(msg.sender, _pid, _amount);
}
withdraw
/**
* @dev Withdraw LP tokens from staking contract and emits an event.
* require statements to revert state when conditions are not met
* checks whether user LP deposit is greater than or equal to withdrawal request amount
* checks whether withdrawal cooldown was initiated
* checks whether user withdrawal request occurs after deposit time
* checks whether current block timestamp is greater than user withdrawal request timestamp plus 48h (enforcing the cooldown period)
* This will transfer any pending rewards to the user
* This will transfer the LP token amount to the user
* This will transfer the peg token to the contract that is proportional to the LP token amount being withdrawn to the user
* This will also adjust user information to reflect latest deposit amounts, and reset withdrawal request time
* @param _pid the pool index
* @param _amount the amount of LP tokens being withdrawn
*/
function withdraw(uint256 _pid, uint256 _amount) public validatePoolByPid(_pid) {
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
require(user.amount >= _amount, "withdraw: can't withdraw more than deposit");
require(user.withdrawRequest != 0 && user.depositTime != 0, "withdraw: cooldown not initiated");
require(user.withdrawRequest > user.depositTime, "withdraw: request before deposit");
require(block.timestamp > (user.withdrawRequest + 172800), "withdraw: cooldown not complete");
uint256 _amountCe = propCeShare(_pid, address(msg.sender), _amount);
require(user.amountCe >= _amountCe, "withdraw: peg token exceeds deposit");
uint256 rewardAmount = computeReward(_pid, address(msg.sender));
safeCeTransfer(msg.sender, rewardAmount);
user.amount = user.amount - _amount;
user.amountCe = user.amountCe - _amountCe;
user.withdrawRequest = 0;
pool.pegToken.safeTransferFrom(address(msg.sender), address(this), _amountCe);
pool.lpToken.safeTransfer(address(msg.sender), _amount);
emit Withdraw(msg.sender, _pid, _amount);
}
withdrawCooldown
/**
* @dev Withdraw cooldown period. Sets withdrawal request timestamp for msg sender.
* require statements to revert state when conditions are not met
* checks user LP amount deposited is greater than 0
* checks user CE share equivalent to LP amount deposited is greater than 0
* checks user withdrawal request time is currently 0 (no pending withdrawal requests)
* checks current block timestamp is greater than user deposit time
* @param _pid the pool index
*/
function withdrawCooldown(uint256 _pid) public validatePoolByPid(_pid) {
UserInfo storage user = userInfo[_pid][msg.sender];
require(user.amount > 0);
require(user.amountCe > 0);
require(user.withdrawRequest == 0);
require(block.timestamp > user.depositTime);
user.withdrawRequest = block.timestamp;
}
propCeShare
/**
* @dev View function to get ce share corresponding to lp amount for withdrawals.
* @param _pid the pool index
* @param _user the address of user
* @param _amount the amount of LP token being withdrawn
* @return amount of ce that corresponds to the lp withdrawal amount
*/
function propCeShare(uint256 _pid, address _user, uint256 _amount) public view validatePoolByPid(_pid) returns (uint256) {
UserInfo storage user = userInfo[_pid][_user];
uint256 propCe = _amount.mul(user.amountCe).div(user.amount);
return propCe;
}
pendingRewards
/**
* @dev View function to get pending Rewards for a user.
* @param _pid the pool index
* @param _user the address of user
* @return the amount of pending ce rewards
*/
function pendingRewards(uint256 _pid, address _user) external view validatePoolByPid(_pid) returns (uint256) {
uint256 rewardAmount = computeReward(_pid, _user);
return rewardAmount;
}
deposited
/**
* @dev View function to get deposited LP for a user.
* @param _pid the pool index
* @param _user the address of user
* @return the amount of LP tokens deposited
*/
function deposited(uint256 _pid, address _user) external view validatePoolByPid(_pid) returns (uint256) {
UserInfo storage user = userInfo[_pid][_user];
return user.amount;
}
depositedCe
/**
* @dev View function to get the deposited CE share for a user.
* @param _pid the pool index
* @param _user the address of user
* @return the amount of CE share corresponding to deposited LP amount at the time of deposit(s)
*/
function depositedCe(uint256 _pid, address _user) external view validatePoolByPid(_pid) returns (uint256) {
UserInfo storage user = userInfo[_pid][_user];
return user.amountCe;
}
depositedTime
/**
* @dev View function to get latest user deposit time
* @param _pid the pool index
* @param _user the address of user
* @return the most recent user deposit timestamp
*/
function depositedTime(uint256 _pid, address _user) external view validatePoolByPid(_pid) returns (uint256) {
UserInfo storage user = userInfo[_pid][_user];
return user.depositTime;
}
depositWithdrawRequest
/**
* @dev View function to get latest user withdrawal request time
* @param _pid the pool index
* @param _user the address of user
* @return the latest user withdrawal request time, can be zero if it hasn't been requested
*/
function depositWithdrawRequest(uint256 _pid, address _user) external view validatePoolByPid(_pid) returns (uint256) {
UserInfo storage user = userInfo[_pid][_user];
return user.withdrawRequest;
}
validatePoolByPid
/**
* @dev Modifier that requires a pool with _pid to exist.
*/
modifier validatePoolByPid(uint256 _pid) {
require (_pid < poolInfo.length , "Pool does not exist") ;
_;
}
safeCeTransfer
/**
* @dev Safe internal transfer function, just in case if rounding error causes pool to not have enough rewards.
* @param _to the recipient address
* @param _amount the amount of rewards
*/
function safeCeTransfer(address _to, uint256 _amount) internal {
uint256 ifBal = CeToken.balanceOf(address(this));
if (_amount > ifBal) {
CeToken.transfer(_to, ifBal);
paidOut += ifBal;
} else {
CeToken.transfer(_to, _amount);
paidOut += _amount;
}
}
Last updated