Skip to content

Instantly share code, notes, and snippets.

@p14041999
Created November 11, 2022 09:25
Show Gist options
  • Select an option

  • Save p14041999/877e0daab347e65be81a72c68f1e3e16 to your computer and use it in GitHub Desktop.

Select an option

Save p14041999/877e0daab347e65be81a72c68f1e3e16 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;
}
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) 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)) users;
modifier onlyOwner(){
require(msg.sender == owner,"STAKE: Action not allowed!");
_;
}
// EVENTS
event UserStaked();
event EpochUpdate();
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
) 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
);
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(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
);
}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);
}
}
function withdrawl(uint amount,bytes32 poolHash) public{
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--;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(0,block.number,0,3000,pools[poolHash].currentEpoch,0);
}else{
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(block.number,0,users[poolHash][msg.sender].totalStakedAmount-amount,pools[poolHash].currentEpoch,0,0);
}
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){
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!");
uint amount = users[poolHash][msg.sender].totalStakedAmount;
uint reward = calculateReward(msg.sender, poolHash);
pools[poolHash].totalNumberOfStakers--;
updateEpoch(poolHash);
users[poolHash][msg.sender] = User(0,block.number,0,3000,pools[poolHash].currentEpoch,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){
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 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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment