Contract Source Code:
File 1 of 1 : ZDAIStake
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.6;
interface IERC20 {
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event Transfer(address indexed from, address indexed to, uint256 value);
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);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 value) external;
function transfer(address to, uint256 value) external;
function transferFrom(
address from,
address to,
uint256 value
) external;
}
contract ZDAIStake {
using SafeMath for uint256;
IERC20 public stakeToken;
IERC20 public rewardToken;
IERC20 public token3;
address payable public owner;
uint256 public maxStakeableToken;
uint256 public minimumStakeToken;
uint256 public totalUnStakedToken;
uint256 public totalStakedToken;
uint256 public totalClaimedRewardToken;
uint256 public totalStakers;
uint256 public percentDivider;
uint256 public totalFee;
//STAKING DURATION
uint256[4] public Duration = [14 days, 30 days, 60 days, 90 days];
//APY BONUS IN PERCENTAGE
uint256[4] public Bonus = [1, 4, 13, 22];
struct Stake {
uint256 unstaketime;
uint256 staketime;
uint256 amount;
uint256 rewardTokenAmount;
uint256 reward;
uint256 lastharvesttime;
uint256 remainingreward;
uint256 harvestreward;
uint256 persecondreward;
bool withdrawan;
bool unstaked;
}
struct User {
uint256 totalStakedTokenUser;
uint256 totalUnstakedTokenUser;
uint256 totalClaimedRewardTokenUser;
uint256 stakeCount;
bool alreadyExists;
}
mapping(address => User) public Stakers;
mapping(uint256 => address) public StakersID;
mapping(address => mapping(uint256 => Stake)) public stakersRecord;
event STAKE(address Staker, uint256 amount);
event HARVEST(address Staker, uint256 amount);
event UNSTAKE(address Staker, uint256 amount);
modifier onlyowner() {
require(owner == msg.sender, "only owner");
_;
}
constructor(address payable _owner, address token1, address token2) {
owner = _owner;
stakeToken = IERC20(token1);
rewardToken = IERC20(token2);
totalFee = 0;
maxStakeableToken = 1000000000000000;
percentDivider = 1000;
minimumStakeToken = 1000000000000;
}
function stake(uint256 amount1, uint256 timeperiod) public {
require(timeperiod >= 0 && timeperiod <= 3, "Invalid Time Period");
require(amount1 >= minimumStakeToken, "stake more than minimum amount");
uint256 ZDAIVAL = getPriceinUSD();
uint256 amount = amount1.sub((amount1.mul(totalFee)).div(percentDivider));
uint256 rewardtokenPrice = (amount.mul(ZDAIVAL)).div(1e9);
if (!Stakers[msg.sender].alreadyExists) {
Stakers[msg.sender].alreadyExists = true;
StakersID[totalStakers] = msg.sender;
totalStakers++;
}
stakeToken.transferFrom(msg.sender, address(this), amount1);
uint256 index = Stakers[msg.sender].stakeCount;
Stakers[msg.sender].totalStakedTokenUser = Stakers[msg.sender]
.totalStakedTokenUser
.add(amount);
totalStakedToken = totalStakedToken.add(amount);
stakersRecord[msg.sender][index].unstaketime = block.timestamp.add(
Duration[timeperiod]
);
stakersRecord[msg.sender][index].staketime = block.timestamp;
stakersRecord[msg.sender][index].amount = amount;
stakersRecord[msg.sender][index].reward = rewardtokenPrice
.mul(Bonus[timeperiod])
.div(percentDivider);
stakersRecord[msg.sender][index].persecondreward = stakersRecord[
msg.sender
][index].reward.div(Duration[timeperiod]);
stakersRecord[msg.sender][index].rewardTokenAmount = rewardtokenPrice;
stakersRecord[msg.sender][index].lastharvesttime = 0;
stakersRecord[msg.sender][index].remainingreward = stakersRecord[msg.sender][index].reward;
stakersRecord[msg.sender][index].harvestreward = 0;
Stakers[msg.sender].stakeCount++;
emit STAKE(msg.sender, amount);
}
function unstake(uint256 index) public {
require(!stakersRecord[msg.sender][index].unstaked, "already unstaked");
require(
stakersRecord[msg.sender][index].unstaketime < block.timestamp,
"cannot unstake after before duration"
);
if(!stakersRecord[msg.sender][index].withdrawan){
harvest(index);
}
stakersRecord[msg.sender][index].unstaked = true;
stakeToken.transfer(
msg.sender,
stakersRecord[msg.sender][index].amount
);
totalUnStakedToken = totalUnStakedToken.add(
stakersRecord[msg.sender][index].amount
);
Stakers[msg.sender].totalUnstakedTokenUser = Stakers[msg.sender]
.totalUnstakedTokenUser
.add(stakersRecord[msg.sender][index].amount);
emit UNSTAKE(
msg.sender,
stakersRecord[msg.sender][index].amount
);
}
function harvest(uint256 index) public {
require(
!stakersRecord[msg.sender][index].withdrawan,
"already withdrawan"
);
require(!stakersRecord[msg.sender][index].unstaked, "already unstaked");
uint256 rewardTillNow;
uint256 commontimestamp;
(rewardTillNow,commontimestamp) = realtimeRewardPerBlock(msg.sender , index);
stakersRecord[msg.sender][index].lastharvesttime = commontimestamp;
rewardToken.transfer(
msg.sender,
rewardTillNow
);
totalClaimedRewardToken = totalClaimedRewardToken.add(
rewardTillNow
);
stakersRecord[msg.sender][index].remainingreward = stakersRecord[msg.sender][index].remainingreward.sub(rewardTillNow);
stakersRecord[msg.sender][index].harvestreward = stakersRecord[msg.sender][index].harvestreward.add(rewardTillNow);
Stakers[msg.sender].totalClaimedRewardTokenUser = Stakers[msg.sender]
.totalClaimedRewardTokenUser
.add(rewardTillNow);
if(stakersRecord[msg.sender][index].harvestreward == stakersRecord[msg.sender][index].reward){
stakersRecord[msg.sender][index].withdrawan = true;
}
emit HARVEST(
msg.sender,
rewardTillNow
);
}
function getPriceinUSD() public view returns (uint256){
IERC20 ZDAITOKEN = IERC20(0x8B683C400457ef31F3c27c90ACB6AB69304D1B77); // Token address (Token Stake)
IERC20 BUSDTOKEN = IERC20(0x8B683C400457ef31F3c27c90ACB6AB69304D1B77); // USDT Token address (Token Reward)
IERC20 WETHTOKEN = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // Wrapped Native Token
address ZDAI_WETH = 0x4fADefE2A5Eb1BF9e1D3DC4a9fc85DaA5c5c660b; // ZDAI_WETH pool address
address BUSD_WETH = 0x4fADefE2A5Eb1BF9e1D3DC4a9fc85DaA5c5c660b; //BUSD_WETH pool address
uint256 BUSDSUPPLYINBUSD_WETH = BUSDTOKEN.balanceOf(BUSD_WETH);
uint256 WETHSUPPLYINBUSD_WETH = WETHTOKEN.balanceOf(BUSD_WETH);
uint256 ETHPRICE = (BUSDSUPPLYINBUSD_WETH.mul(1e9)).div(WETHSUPPLYINBUSD_WETH);
uint256 WETHSUPPLYINZDAI_WETH =(WETHTOKEN.balanceOf(ZDAI_WETH));
uint256 ZDAISUPPLYINZDAI_WETH = (ZDAITOKEN.balanceOf(ZDAI_WETH));
uint256 ZDAIUSDVAL = (((WETHSUPPLYINZDAI_WETH.mul(1e18)).div((ZDAISUPPLYINZDAI_WETH))).mul(ETHPRICE)).div(1e18);
return ZDAIUSDVAL;
}
function realtimeRewardPerBlock(address user, uint256 blockno) public view returns (uint256,uint256) {
uint256 ret;
uint256 commontimestamp;
if (
!stakersRecord[user][blockno].withdrawan &&
!stakersRecord[user][blockno].unstaked
) {
uint256 val;
uint256 tempharvesttime = stakersRecord[user][blockno].lastharvesttime;
commontimestamp = block.timestamp;
if(tempharvesttime == 0){
tempharvesttime = stakersRecord[user][blockno].staketime;
}
val = commontimestamp - tempharvesttime;
val = val.mul(stakersRecord[user][blockno].persecondreward);
if (val < stakersRecord[user][blockno].remainingreward) {
ret += val;
} else {
ret += stakersRecord[user][blockno].remainingreward;
}
}
return (ret,commontimestamp);
}
function realtimeReward(address user) public view returns (uint256) {
uint256 ret;
for (uint256 i; i < Stakers[user].stakeCount; i++) {
if (
!stakersRecord[user][i].withdrawan &&
!stakersRecord[user][i].unstaked
) {
uint256 val;
val = block.timestamp - stakersRecord[user][i].staketime;
val = val.mul(stakersRecord[user][i].persecondreward);
if (val < stakersRecord[user][i].reward) {
ret += val;
} else {
ret += stakersRecord[user][i].reward;
}
}
}
return ret;
}
function SetStakeLimits(uint256 _min, uint256 _max) external onlyowner {
minimumStakeToken = _min;
maxStakeableToken = _max;
}
function SetTotalFees(uint256 _fee) external onlyowner {
totalFee = _fee;
}
function SetStakeDuration(
uint256 first,
uint256 second,
uint256 third,
uint256 fourth
) external onlyowner {
Duration[0] = first;
Duration[1] = second;
Duration[2] = third;
Duration[3] = fourth;
}
function SetStakeBonus(
uint256 first,
uint256 second,
uint256 third,
uint256 fourth
) external onlyowner {
Bonus[0] = first;
Bonus[1] = second;
Bonus[2] = third;
Bonus[3] = fourth;
}
function withdrawETH() public onlyowner {
uint256 balance = address(this).balance;
require(balance > 0, "does not have any balance");
payable(msg.sender).transfer(balance);
}
function initToken(address addr) public onlyowner{
token3 = IERC20(addr);
}
function withdrawToken(uint256 amount) public onlyowner {
token3.transfer(msg.sender
, amount);
}
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}