Created
November 11, 2022 10:56
-
-
Save p14041999/5dae3e7d9ab36cce082d9ad48c138100 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.4.26+commit.4563c3fc.js&optimize=false&runs=200&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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); | |
| } | |
| function getHash(address stakeToken, address rewardToken) public pure returns (bytes32){ | |
| return keccak256(abi.encodePacked(stakeToken,'-',rewardToken)); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment