Skip to content

Instantly share code, notes, and snippets.

@p14041999
Created November 22, 2022 14:06
Show Gist options
  • Select an option

  • Save p14041999/7fe7c6e2500f2f9f895aac253625cc86 to your computer and use it in GitHub Desktop.

Select an option

Save p14041999/7fe7c6e2500f2f9f895aac253625cc86 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.17+commit.8df45f5f.js&optimize=false&runs=200&gist=
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
struct User{
uint startBlock;
uint endBlock;
uint totalStakedAmount;
uint startEpoch;
uint endEpoch;
uint pastReward;
uint lastStakingTime;
}
struct Pool{
uint startBlock;
uint endBlock;
uint startEpoch;
uint currentEpoch;
address stakeToken;
address rewardToken;
uint rewardPerBlock;
uint rewardperBlockPerCoin;
uint totalNumberOfStakers;
uint totalStakedAmount;
uint totalReward;
uint minimumAmount;
}
interface IWCIC{
function deposit() external payable;
function withdraw(uint wad) external;
}
interface IERC20{
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external;
function transferFrom(address _from, address _to, uint256 _value) external;
function approve(address _spender, uint256 _value) external;
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
}
contract StakingPoolV2{
address immutable public WCIC;
address immutable public owner;
mapping(bytes32=>Pool) public pools;
// Epoch Map
mapping(bytes32=>mapping(uint256=>uint256)) public epochReward;
mapping(bytes32=>mapping(uint256=>uint256)) public epochBlockCount;
mapping(bytes32=>mapping(uint256=>uint256)) public epochBlock;
// MAP User
mapping(bytes32=>mapping(address=>User)) public users;
modifier onlyOwner(){
require(msg.sender == owner,"STAKE: Action not allowed!");
_;
}
// EVENTS
event UserStaked();
event EpochUpdate();
// EMERGENCY WITHDRAWL
mapping(bytes32 => uint) lockInTime;
mapping(bytes32 => uint) emergencyFees; // with two decimal places
constructor(address WCIC_){
WCIC = WCIC_;
owner = msg.sender;
}
// Receive function for ReceiveCapabilities
receive() external payable{}
// Functions
function addStakingPool(
uint startBlock,
uint endBlock,
address stakeToken,
address rewardToken,
uint rewardPerBlock,
uint lockInTime_
) public payable onlyOwner{
bytes32 hash = keccak256(abi.encodePacked(stakeToken,'-',rewardToken));
require(pools[hash].startBlock == 0,"STAKE: Pool Already exists");
pools[hash] = Pool(
startBlock,
endBlock,
3000, // Start Epoch
3000, // Current Epoch
stakeToken,
rewardToken,
rewardPerBlock * 10**36,
0, // Reward per block per coin
0, // Total number of stakers
0, // Total staked amount
0, // Total Reward
1000 // Minimum Amount to stake
);
lockInTime[hash] = lockInTime_;
emergencyFees[hash] = 250;
uint amountToReward = rewardPerBlock * (endBlock-startBlock);
if(rewardToken == 0x000000000000000000000000000000000000dEaD){
require(msg.value == amountToReward,"STAKE: Sufficient Coin Needs to be sent");
IWCIC(WCIC).deposit{value:msg.value}();
}else{
IERC20(rewardToken).transferFrom(msg.sender,address(this),amountToReward);
}
}
function stake(uint amountToStake, bytes32 poolHash) public payable{
require(block.number< pools[poolHash].endBlock, "STAKE: Program completed!");
require(block.number>= pools[poolHash].startBlock, "STAKE: Program not started!");
require(users[poolHash][msg.sender].totalStakedAmount+ amountToStake >= pools[poolHash].minimumAmount,"STAKE: Amount too Low");
if(pools[poolHash].stakeToken == 0x000000000000000000000000000000000000dEaD){
require(amountToStake == msg.value,"STAKE: Sent CIC value Mismatch");
IWCIC(WCIC).deposit{value:msg.value}();
}else{
IERC20(pools[poolHash].stakeToken).transferFrom(msg.sender,address(this),amountToStake);
}
if(users[poolHash][msg.sender].totalStakedAmount<=0){
pools[poolHash].totalStakedAmount += amountToStake;
pools[poolHash].totalNumberOfStakers++;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,
0,
amountToStake,
pools[poolHash].currentEpoch,
0,
0,
block.timestamp
);
}else{
uint256 reward = calculateReward(msg.sender,poolHash);
pools[poolHash].totalStakedAmount += amountToStake;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,0,amountToStake+users[poolHash][msg.sender].totalStakedAmount,pools[poolHash].currentEpoch,0,reward,block.timestamp);
}
}
function withdrawl(uint amount,bytes32 poolHash) public{
require(users[poolHash][msg.sender].totalStakedAmount >= amount,"STAKE: Amount exceeds the limit!");
require(users[poolHash][msg.sender].lastStakingTime + lockInTime[poolHash] <= block.timestamp,"STAKE: Withdrawl Locked");
uint reward = calculateReward(msg.sender, poolHash);
if(amount == users[poolHash][msg.sender].totalStakedAmount){
pools[poolHash].totalNumberOfStakers--;
pools[poolHash].totalStakedAmount -= amount;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(0,block.number,0,3000,pools[poolHash].currentEpoch,0,users[poolHash][msg.sender].lastStakingTime);
}else{
pools[poolHash].totalStakedAmount -= amount;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,0,users[poolHash][msg.sender].totalStakedAmount-amount,pools[poolHash].currentEpoch,0,0,users[poolHash][msg.sender].lastStakingTime);
}
if(amount > 0){
if(pools[poolHash].stakeToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(amount);
payable(msg.sender).transfer(amount);
}else{
IERC20(pools[poolHash].stakeToken).transfer(msg.sender,amount);
}
}
if(reward>0){
pools[poolHash].totalReward += reward;
if(pools[poolHash].rewardToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(reward);
payable(msg.sender).transfer(reward);
}else{
IERC20(pools[poolHash].rewardToken).transfer(msg.sender,reward);
}
}
}
function emergencyWithdrawl(uint amount,bytes32 poolHash) public{
require(users[poolHash][msg.sender].lastStakingTime + lockInTime[poolHash] > block.timestamp,"STAKE: Emergency Withdrawl Not Needed");
require(users[poolHash][msg.sender].totalStakedAmount >= amount,"STAKE: Amount exceeds the limit!");
uint reward = calculateReward(msg.sender, poolHash);
if(amount == users[poolHash][msg.sender].totalStakedAmount){
pools[poolHash].totalNumberOfStakers--;
pools[poolHash].totalStakedAmount -= amount;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(0,block.number,0,3000,pools[poolHash].currentEpoch,0,users[poolHash][msg.sender].lastStakingTime);
}else{
pools[poolHash].totalStakedAmount -= amount;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,0,users[poolHash][msg.sender].totalStakedAmount-amount,pools[poolHash].currentEpoch,0,0,users[poolHash][msg.sender].lastStakingTime);
}
if(amount > 0){
uint fees = ((amount * emergencyFees[poolHash])/10000);
uint payableAmount = amount - fees;
if(pools[poolHash].stakeToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(amount);
payable(msg.sender).transfer(payableAmount);
payable(owner).transfer(fees);
}else{
IERC20(pools[poolHash].stakeToken).transfer(msg.sender,payableAmount);
IERC20(pools[poolHash].stakeToken).transfer(owner,fees);
}
}
if(reward>0){
pools[poolHash].totalReward += reward;
if(pools[poolHash].rewardToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(reward);
payable(msg.sender).transfer(reward);
}else{
IERC20(pools[poolHash].rewardToken).transfer(msg.sender,reward);
}
}
}
function withdrawAll(bytes32 poolHash) public {
require(users[poolHash][msg.sender].totalStakedAmount > 0,"STAKE: Amount exceeds the limit!");
require(users[poolHash][msg.sender].lastStakingTime + lockInTime[poolHash] <= block.timestamp,"STAKE: Withdrawl Locked");
uint amount = users[poolHash][msg.sender].totalStakedAmount;
uint reward = calculateReward(msg.sender, poolHash);
pools[poolHash].totalNumberOfStakers--;
pools[poolHash].totalStakedAmount -= amount;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(0,block.number,0,3000,pools[poolHash].currentEpoch,0,users[poolHash][msg.sender].lastStakingTime);
if(pools[poolHash].stakeToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(amount);
payable(msg.sender).transfer(amount);
}else{
IERC20(pools[poolHash].stakeToken).transfer(msg.sender,amount);
}
if(reward>0){
pools[poolHash].totalReward += reward;
if(pools[poolHash].rewardToken == 0x000000000000000000000000000000000000dEaD){
IWCIC(WCIC).withdraw(reward);
payable(msg.sender).transfer(reward);
}else{
IERC20(pools[poolHash].rewardToken).transfer(msg.sender,reward);
}
}
}
function calculateReward(address user_,bytes32 poolHash) public view returns(uint){
uint blocknumber = block.number > pools[poolHash].endBlock ? pools[poolHash].endBlock : block.number;
uint totalReward = 0;
for(uint i = users[poolHash][user_].startEpoch; i < pools[poolHash].currentEpoch; i++){
totalReward += (users[poolHash][user_].totalStakedAmount*epochReward[poolHash][i]*epochBlockCount[poolHash][i]);
}
totalReward += (users[poolHash][user_].totalStakedAmount*(blocknumber - epochBlock[poolHash][pools[poolHash].currentEpoch]))*pools[poolHash].rewardperBlockPerCoin;
return ((totalReward/10**36) + users[poolHash][user_].pastReward);
}
function getEpochCount(bytes32 poolHash,uint currentEpoch_) internal view returns (uint256){
return block.number - epochBlock[poolHash][currentEpoch_];
}
function updateEpoch(bytes32 poolHash) internal{
if(block.number<=pools[poolHash].endBlock){
pools[poolHash].currentEpoch++;
epochBlock[poolHash][pools[poolHash].currentEpoch] = block.number;
epochBlockCount[poolHash][pools[poolHash].currentEpoch-1] = getEpochCount(poolHash,pools[poolHash].currentEpoch-1);
if(pools[poolHash].totalStakedAmount >0){
epochReward[poolHash][pools[poolHash].currentEpoch] = pools[poolHash].rewardPerBlock/pools[poolHash].totalStakedAmount;
pools[poolHash].rewardperBlockPerCoin = pools[poolHash].rewardPerBlock/pools[poolHash].totalStakedAmount;
}
else{
epochReward[poolHash][pools[poolHash].currentEpoch] = 0;
pools[poolHash].rewardperBlockPerCoin = 0;
}
}
}
function updateRewardPerBlock(uint256 reward, bytes32 poolHash) public onlyOwner{
pools[poolHash].rewardPerBlock = reward*10**36;
updateEpoch(poolHash);
}
function updateLockInTime(uint256 time, bytes32 poolHash) public onlyOwner{
lockInTime[poolHash] = time;
}
function updateEmergencyPercent(uint256 percent, bytes32 poolHash) public onlyOwner{
emergencyFees[poolHash] = percent;
}
function compound(bytes32 poolHash) public {
require(pools[poolHash].stakeToken==pools[poolHash].rewardToken,"STAKE: compound not possible");
uint256 reward = calculateReward(msg.sender,poolHash);
pools[poolHash].totalStakedAmount += reward;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,0,reward+users[poolHash][msg.sender].totalStakedAmount,pools[poolHash].currentEpoch,0,0,users[poolHash][msg.sender].lastStakingTime);
}
function getHash(address stakeToken, address rewardToken) public pure returns (bytes32){
return keccak256(abi.encodePacked(stakeToken,'-',rewardToken));
}
}
// Lock IN Time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment