// File: @openzeppelin/contracts/utils/math/SafeMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// 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 (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}
pragma solidity 0.8.0;
interface IStaking {
function getEpochId(uint timestamp) external view returns (uint); // get epoch id
function getEpochUserBalance(address user, address token, uint128 epoch) external view returns(uint);
function getEpochPoolSize(address token, uint128 epoch) external view returns (uint);
function epoch1Start() external view returns (uint);
function epochDuration() external view returns (uint);
function hasReferrer(address addr) external view returns(bool);
function referrals(address addr) external view returns(address);
function firstReferrerRewardPercentage() external view returns(uint256);
function secondReferrerRewardPercentage() external view returns(uint256);
function isStakeFinished(address staker) external view returns (bool);
function stakeData(address staker) external view returns (uint256 startEpoch, uint256 endEpoch, bool active);
function stakeEndEpoch(address staker) external view returns (uint128);
function calcDurationBonusMultiplier(uint128 epochId, address staker) external view returns (uint256);
}
interface Minter {
function mint(address to, uint256 amount) external;
}
contract SwappYieldFarm {
// lib
using SafeMath for uint;
using SafeMath for uint128;
// constants
uint public constant NR_OF_EPOCHS = 60;
uint256 constant private CALC_MULTIPLIER = 1000000;
// addreses
address private _swappAddress = 0x8CB924583681cbFE487A62140a994A49F833c244;
address private _owner;
bool private _paused = false;
// contracts
IStaking private _staking;
Minter private _minter;
uint[] private epochs = new uint[](NR_OF_EPOCHS + 1);
uint128 public lastInitializedEpoch;
mapping(address => uint128) public lastEpochIdHarvested;
uint public epochDuration; // init from staking contract
uint public epochStart; // init from staking contract
mapping(uint128 => uint256) public epochAmounts;
mapping(uint128 => uint256) public epochDurationBonus;
mapping(address => uint256) public collectedDurationBonus;
modifier onlyStaking() {
require(msg.sender == address(_staking), "Only staking contract can perfrom this action");
_;
}
modifier onlyOwner() {
require(msg.sender == _owner, "Only owner can perfrom this action");
_;
}
modifier whenNotPaused() {
require(!paused(), "Contract is paused");
_;
}
// events
event MassHarvest(address indexed user, uint256 epochsHarvested, uint256 totalValue);
event Harvest(address indexed user, uint128 indexed epochId, uint256 amount);
event ReferrerRewardCollected(address indexed staker, address indexed referrer, uint256 rewardAmount);
event Referrer2RewardCollected(address indexed staker, address indexed referrer, address indexed referrer2, uint256 rewardAmount);
event DurationBonusCollected(address indexed staker, uint128 indexed epochId, uint256 bonusAmount);
event DurationBonusDistributed(address indexed staker, uint128 indexed epochId, uint256 bonusAmount);
event DurationBonusLost(address indexed staker, uint128 indexed epochId, uint256 bonusAmount);
// constructor
constructor() {
_staking = IStaking(0x60F4D3e409Ad2Bb6BF5edFBCC85691eE1977cf35);
_minter = Minter(0xBC1f9993ea5eE2C77909bf43d7a960bB8dA8C9B9);
epochDuration = _staking.epochDuration();
epochStart = _staking.epoch1Start();
_owner = msg.sender;
_initEpochReward();
_initDurationBonus();
}
function setEpochAmount(uint128 epochId, uint256 amount) external onlyOwner {
require(epochId > 0 && epochId <= NR_OF_EPOCHS, "Minimum epoch number is 1 and Maximum number of epochs is 60");
require(epochId > _getEpochId(), "Only future epoch can be updated");
epochAmounts[epochId] = amount;
}
function setEpochDurationBonus(uint128 epochId, uint256 amount) external onlyOwner {
require(epochId > 0 && epochId <= NR_OF_EPOCHS, "Minimum epoch number is 1 and Maximum number of epochs is 60");
require(epochId > _getEpochId(), "Only future epoch can be updated");
epochDurationBonus[epochId] = amount;
}
function getTotalAmountPerEpoch(uint128 epoch) public view returns (uint) {
return epochAmounts[epoch].mul(10**18);
}
function getDurationBonusPerEpoch(uint128 epoch) public view returns (uint) {
return epochDurationBonus[epoch].mul(10**18);
}
function getCurrentEpochAmount() public view returns (uint) {
uint128 currentEpoch = _getEpochId();
if (currentEpoch <= 0 || currentEpoch > NR_OF_EPOCHS) {
return 0;
}
return epochAmounts[currentEpoch];
}
function getCurrentEpochDurationBonus() public view returns (uint) {
uint128 currentEpoch = _getEpochId();
if (currentEpoch <= 0 || currentEpoch > NR_OF_EPOCHS) {
return 0;
}
return epochDurationBonus[currentEpoch];
}
function getTotalDistributedAmount() external view returns(uint256) {
uint256 totalDistributed;
for (uint128 i = 1; i <= NR_OF_EPOCHS; i++) {
totalDistributed += epochAmounts[i];
}
return totalDistributed;
}
function getTotalDurationBonus() external view returns(uint256) {
uint256 totalBonus;
for (uint128 i = 1; i <= NR_OF_EPOCHS; i++) {
totalBonus += epochDurationBonus[i];
}
return totalBonus;
}
// public methods
// public method to harvest all the unharvested epochs until current epoch - 1
function massHarvest() external whenNotPaused returns (uint){
uint totalDistributedValue;
uint epochId = _getEpochId().sub(1); // fails in epoch 0
// force max number of epochs
if (epochId > NR_OF_EPOCHS) {
epochId = NR_OF_EPOCHS;
}
for (uint128 i = lastEpochIdHarvested[msg.sender] + 1; i <= epochId; i++) {
// i = epochId
// compute distributed Value and do one single transfer at the end
totalDistributedValue += _harvest(i);
uint256 durationBonus = _calcDurationBonus(i);
if (durationBonus > 0) {
collectedDurationBonus[msg.sender] = collectedDurationBonus[msg.sender].add(durationBonus);
emit DurationBonusCollected(msg.sender, i, durationBonus);
}
}
emit MassHarvest(msg.sender, epochId - lastEpochIdHarvested[msg.sender], totalDistributedValue);
uint256 totalDurationBonus = 0;
if (_staking.isStakeFinished(msg.sender) && collectedDurationBonus[msg.sender] > 0) {
totalDurationBonus = collectedDurationBonus[msg.sender];
collectedDurationBonus[msg.sender] = 0;
_minter.mint(msg.sender, totalDurationBonus);
emit DurationBonusDistributed(msg.sender, _getEpochId(), totalDurationBonus);
}
if (totalDistributedValue > 0) {
_minter.mint(msg.sender, totalDistributedValue);
//Referrer reward
distributeReferrerReward(totalDistributedValue.add(totalDurationBonus));
}
return totalDistributedValue.add(totalDurationBonus);
}
function harvest (uint128 epochId) external whenNotPaused returns (uint){
// checks for requested epoch
require (_getEpochId() > epochId, "This epoch is in the future");
require(epochId <= NR_OF_EPOCHS, "Maximum number of epochs is 60");
require (lastEpochIdHarvested[msg.sender].add(1) == epochId, "Harvest in order");
uint userReward = _harvest(epochId);
uint256 durationBonus = _calcDurationBonus(epochId);
collectedDurationBonus[msg.sender] = collectedDurationBonus[msg.sender].add(_calcDurationBonus(epochId));
emit DurationBonusCollected(msg.sender, epochId, durationBonus);
uint256 totalDurationBonus = 0;
if (_staking.isStakeFinished(msg.sender) && collectedDurationBonus[msg.sender] > 0) {
totalDurationBonus = collectedDurationBonus[msg.sender];
collectedDurationBonus[msg.sender] = 0;
_minter.mint(msg.sender, totalDurationBonus);
emit DurationBonusDistributed(msg.sender, epochId, totalDurationBonus);
}
if (userReward > 0) {
_minter.mint(msg.sender, userReward);
//Referrer reward
distributeReferrerReward(userReward.add(totalDurationBonus));
}
emit Harvest(msg.sender, epochId, userReward);
return userReward.add(totalDurationBonus);
}
function distributeReferrerReward(uint256 stakerReward) internal {
if (_staking.hasReferrer(msg.sender)) {
address referrer = _staking.referrals(msg.sender);
uint256 ref1Reward = stakerReward.mul(_staking.firstReferrerRewardPercentage()).div(10000);
_minter.mint(referrer, ref1Reward);
emit ReferrerRewardCollected(msg.sender, referrer, ref1Reward);
// second step referrer
if (_staking.hasReferrer(referrer)) {
address referrer2 = _staking.referrals(referrer);
uint256 ref2Reward = stakerReward.mul(_staking.secondReferrerRewardPercentage()).div(10000);
_minter.mint(referrer2, ref2Reward);
emit Referrer2RewardCollected(msg.sender, referrer, referrer2, ref2Reward);
}
}
}
// views
// calls to the staking smart contract to retrieve the epoch total pool size
function getPoolSize(uint128 epochId) external view returns (uint) {
return _getPoolSize(epochId);
}
function getCurrentEpoch() external view returns (uint) {
return _getEpochId();
}
// calls to the staking smart contract to retrieve user balance for an epoch
function getEpochStake(address userAddress, uint128 epochId) external view returns (uint) {
return _getUserBalancePerEpoch(userAddress, epochId);
}
function userLastEpochIdHarvested() external view returns (uint){
return lastEpochIdHarvested[msg.sender];
}
function getUserLastEpochHarvested(address staker) external view returns (uint) {
return lastEpochIdHarvested[staker];
}
function estimateDurationBonus (uint128 epochId) public view returns (uint) {
uint256 poolSize = _getPoolSize(epochId);
// exit if there is no stake on the epoch
if (poolSize == 0) {
return 0;
}
uint256 stakerMultiplier = stakerDurationMultiplier(msg.sender, epochId + 1);
return getDurationBonusPerEpoch(epochId)
.mul(_getUserBalancePerEpoch(msg.sender, epochId))
.div(poolSize).mul(stakerMultiplier).div(CALC_MULTIPLIER);
}
function stakerDurationMultiplier(address staker, uint128 epochId) public view returns (uint256) {
(uint256 startEpoch, uint256 endEpoch, bool active) = _staking.stakeData(staker);
if (epochId > endEpoch || (epochId <= endEpoch && active == false) || epochId < startEpoch) {
return 0;
}
uint256 stakerMultiplier = _staking.calcDurationBonusMultiplier(epochId, staker);
return stakerMultiplier;
}
function reduceDurationBonus(address staker, uint256 reduceMultiplier) public onlyStaking {
uint256 collected = collectedDurationBonus[staker];
if (collected > 0) {
collectedDurationBonus[staker] = collected.mul(reduceMultiplier).div(CALC_MULTIPLIER);
uint256 bonusLost = collected.sub(collectedDurationBonus[staker]);
DurationBonusLost(staker, _getEpochId(), bonusLost);
}
}
function clearDurationBonus(address staker) public onlyStaking {
uint256 collected = collectedDurationBonus[staker];
if (collected > 0) {
collectedDurationBonus[staker] = 0;
DurationBonusLost(staker, _getEpochId(), collected);
}
}
// internal methods
function _initEpoch(uint128 epochId) internal {
require(lastInitializedEpoch.add(1) == epochId, "Epoch can be init only in order");
lastInitializedEpoch = epochId;
// call the staking smart contract to init the epoch
epochs[epochId] = _getPoolSize(epochId);
}
function _harvest (uint128 epochId) internal returns (uint) {
// try to initialize an epoch. if it can't it fails
// if it fails either user either a Swapp account will init not init epochs
if (lastInitializedEpoch < epochId) {
_initEpoch(epochId);
}
// Set user state for last harvested
lastEpochIdHarvested[msg.sender] = epochId;
// compute and return user total reward. For optimization reasons the transfer have been moved to an upper layer (i.e. massHarvest needs to do a single transfer)
// exit if there is no stake on the epoch
if (epochs[epochId] == 0) {
return 0;
}
uint128 endEpoch = _staking.stakeEndEpoch(msg.sender);
if (epochId >= endEpoch) {
return 0;
}
return getTotalAmountPerEpoch(epochId)
.mul(_getUserBalancePerEpoch(msg.sender, epochId))
.div(epochs[epochId]);
}
function _calcDurationBonus(uint128 epochId) internal view returns (uint) {
// exit if there is no stake on the epoch
if (epochs[epochId] == 0) {
return 0;
}
uint256 stakerMultiplier = stakerDurationMultiplier(msg.sender, epochId + 1);
return getDurationBonusPerEpoch(epochId)
.mul(_getUserBalancePerEpoch(msg.sender, epochId))
.div(epochs[epochId]).mul(stakerMultiplier).div(CALC_MULTIPLIER);
}
function _getPoolSize(uint128 epochId) internal view returns (uint) {
// retrieve token balance
return _staking.getEpochPoolSize(_swappAddress, epochId);
}
function _getUserBalancePerEpoch(address userAddress, uint128 epochId) internal view returns (uint){
// retrieve token balance per user per epoch
return _staking.getEpochUserBalance(userAddress, _swappAddress, epochId);
}
// compute epoch id from blocktimestamp and epochstart date
function _getEpochId() internal view returns (uint128 epochId) {
if (block.timestamp < epochStart) {
return 0;
}
epochId = uint128(block.timestamp.sub(epochStart).div(epochDuration).add(1));
}
function paused() public view returns (bool) {
return _paused;
}
function pause() external onlyOwner {
_paused = true;
}
function unpause() external onlyOwner {
_paused = false;
}
function _initEpochReward() internal {
epochAmounts[1] = 5000000;
epochAmounts[2] = 2000000;
epochAmounts[3] = 2000000;
epochAmounts[4] = 2000000;
epochAmounts[5] = 2000000;
epochAmounts[6] = 2000000;
epochAmounts[7] = 1500000;
epochAmounts[8] = 1500000;
epochAmounts[9] = 1500000;
epochAmounts[10] = 1500000;
epochAmounts[11] = 1500000;
epochAmounts[12] = 1500000;
epochAmounts[13] = 500000;
epochAmounts[14] = 500000;
epochAmounts[15] = 500000;
epochAmounts[16] = 500000;
epochAmounts[17] = 500000;
epochAmounts[18] = 500000;
epochAmounts[19] = 500000;
epochAmounts[20] = 500000;
epochAmounts[21] = 500000;
epochAmounts[22] = 500000;
epochAmounts[23] = 500000;
epochAmounts[24] = 500000;
epochAmounts[25] = 400000;
epochAmounts[26] = 400000;
epochAmounts[27] = 400000;
epochAmounts[28] = 400000;
epochAmounts[29] = 400000;
epochAmounts[30] = 400000;
epochAmounts[31] = 400000;
epochAmounts[32] = 400000;
epochAmounts[33] = 400000;
epochAmounts[34] = 400000;
epochAmounts[35] = 400000;
epochAmounts[36] = 400000;
epochAmounts[37] = 250000;
epochAmounts[38] = 250000;
epochAmounts[39] = 250000;
epochAmounts[40] = 250000;
epochAmounts[41] = 250000;
epochAmounts[42] = 250000;
epochAmounts[43] = 250000;
epochAmounts[44] = 250000;
epochAmounts[45] = 250000;
epochAmounts[46] = 250000;
epochAmounts[47] = 250000;
epochAmounts[48] = 250000;
epochAmounts[49] = 250000;
epochAmounts[50] = 250000;
epochAmounts[51] = 250000;
epochAmounts[52] = 250000;
epochAmounts[53] = 250000;
epochAmounts[54] = 250000;
epochAmounts[55] = 250000;
epochAmounts[56] = 250000;
epochAmounts[57] = 250000;
epochAmounts[58] = 250000;
epochAmounts[59] = 250000;
epochAmounts[60] = 250000;
}
function _initDurationBonus() internal {
epochDurationBonus[1] = 21450;
epochDurationBonus[2] = 23595;
epochDurationBonus[3] = 25954;
epochDurationBonus[4] = 28550;
epochDurationBonus[5] = 31405;
epochDurationBonus[6] = 34545;
epochDurationBonus[7] = 38000;
epochDurationBonus[8] = 41800;
epochDurationBonus[9] = 45980;
epochDurationBonus[10] = 50578;
epochDurationBonus[11] = 55635;
epochDurationBonus[12] = 61477;
epochDurationBonus[13] = 67932;
epochDurationBonus[14] = 75065;
epochDurationBonus[15] = 82947;
epochDurationBonus[16] = 91656;
epochDurationBonus[17] = 101280;
epochDurationBonus[18] = 111915;
epochDurationBonus[19] = 123666;
epochDurationBonus[20] = 136651;
epochDurationBonus[21] = 150999;
epochDurationBonus[22] = 166854;
epochDurationBonus[23] = 184374;
epochDurationBonus[24] = 204701;
epochDurationBonus[25] = 227269;
epochDurationBonus[26] = 252326;
epochDurationBonus[27] = 280145;
epochDurationBonus[28] = 311031;
epochDurationBonus[29] = 345322;
epochDurationBonus[30] = 383393;
epochDurationBonus[31] = 425662;
epochDurationBonus[32] = 472592;
epochDurationBonus[33] = 524695;
epochDurationBonus[34] = 582543;
epochDurationBonus[35] = 646768;
epochDurationBonus[36] = 721639;
epochDurationBonus[37] = 805178;
epochDurationBonus[38] = 898388;
epochDurationBonus[39] = 1002387;
epochDurationBonus[40] = 1118426;
epochDurationBonus[41] = 1247898;
epochDurationBonus[42] = 1392358;
epochDurationBonus[43] = 1553541;
epochDurationBonus[44] = 1733382;
epochDurationBonus[45] = 1934043;
epochDurationBonus[46] = 2157933;
epochDurationBonus[47] = 2407740;
epochDurationBonus[48] = 2700403;
epochDurationBonus[49] = 3028638;
epochDurationBonus[50] = 3396771;
epochDurationBonus[51] = 3809651;
epochDurationBonus[52] = 4272716;
epochDurationBonus[53] = 4792068;
epochDurationBonus[54] = 5374546;
epochDurationBonus[55] = 6027826;
epochDurationBonus[56] = 6760512;
epochDurationBonus[57] = 7582256;
epochDurationBonus[58] = 8503884;
epochDurationBonus[59] = 9537537;
epochDurationBonus[60] = 11000000;
}
}
{
"compilationTarget": {
"SwappYieldFarm.sol": "SwappYieldFarm"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"uint128","name":"epochId","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"bonusAmount","type":"uint256"}],"name":"DurationBonusCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"uint128","name":"epochId","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"bonusAmount","type":"uint256"}],"name":"DurationBonusDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"uint128","name":"epochId","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"bonusAmount","type":"uint256"}],"name":"DurationBonusLost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"uint128","name":"epochId","type":"uint128"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"epochsHarvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalValue","type":"uint256"}],"name":"MassHarvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":true,"internalType":"address","name":"referrer2","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"Referrer2RewardCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"referrer","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"ReferrerRewardCollected","type":"event"},{"inputs":[],"name":"NR_OF_EPOCHS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"clearDurationBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"collectedDurationBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"","type":"uint128"}],"name":"epochAmounts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"","type":"uint128"}],"name":"epochDurationBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"estimateDurationBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpochAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpochDurationBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"epoch","type":"uint128"}],"name":"getDurationBonusPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"getEpochStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"getPoolSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"epoch","type":"uint128"}],"name":"getTotalAmountPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDistributedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalDurationBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getUserLastEpochHarvested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"harvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastEpochIdHarvested","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastInitializedEpoch","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"massHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"reduceMultiplier","type":"uint256"}],"name":"reduceDurationBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"epochId","type":"uint128"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setEpochAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"epochId","type":"uint128"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setEpochDurationBonus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint128","name":"epochId","type":"uint128"}],"name":"stakerDurationMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userLastEpochIdHarvested","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]