编译器
0.8.21+commit.d9974bed
文件 1 的 10:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
文件 3 的 10:IDeltaRewardPoolMultiple.sol
pragma solidity ^0.8.21;
interface IDeltaRewardPoolMultiple {
function getUserStakeInfo(
address user,
uint256 positionID
)
external
view
returns (
uint256 power,
uint256 amount,
uint256 stakeTime,
uint256 stakeDuration
);
}
文件 4 的 10:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 5 的 10:IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 6 的 10:Manager.sol
pragma solidity >=0.8.19;
pragma abicoder v2;
import "@openzeppelin/contracts/utils/Context.sol";
abstract contract Manager is Context {
mapping(address => bool) private _accounts;
modifier onlyManager {
require(isManager(), "only manager");
_;
}
constructor() {
_accounts[_msgSender()] = true;
}
function isManager(address one) public view returns (bool) {
return _accounts[one];
}
function isManager() public view returns (bool) {
return isManager(_msgSender());
}
function setManager(address one, bool val) public onlyManager {
require(one != address(0), "address is zero");
_accounts[one] = val;
}
function setManagerBatch(address[] calldata list, bool val) public onlyManager {
for (uint256 i = 0; i < list.length; i++) {
setManager(list[i], val);
}
}
}
文件 7 的 10:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1, "Math: mulDiv overflow");
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
文件 8 的 10:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 9 的 10:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
文件 10 的 10:UXLINKTokenRewardPoolMultiple.sol
pragma solidity ^0.8.21;
pragma abicoder v2;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IDeltaRewardPoolMultiple} from "../libs/IDeltaRewardPoolMultiple.sol";
import {Manager} from "../libs/Manager.sol";
contract UXLINKTokenRewardPoolMultiple is IDeltaRewardPoolMultiple, ReentrancyGuard, Manager {
using Address for address;
using SafeERC20 for IERC20;
bool private initialized;
bool public withdrawOpened;
uint256 public constant monthTime = 30 days;
struct UserInfo {
uint256 amount;
uint256 stakeTime;
uint256 stakeDuration;
uint256 power;
uint256 reward;
uint256 allReward;
uint256 rewardPerTokenPaid;
}
address public devAddress;
uint256 public constant MIN_DEPOSIT_AMOUNT = 0.00001 ether;
uint256 public constant MIN_WITHDRAW_AMOUNT = 0.00001 ether;
uint256 public constant basRate = 100000;
uint256 public punishRate = 10000;
uint256[] public stakeTimeRatio;
address public rewardToken;
address public stakedToken;
uint256 public totalReward;
uint256 public curCycleStartTime;
uint256 public startStakeTime;
uint256 public poolSurplusReward;
uint256 public curCycleReward;
uint256 public nextCycleReward;
uint256 public nextDuration;
uint256 public cycleTimes;
uint256 public periodFinish;
uint256 public totalPower;
uint256 public totalAmount;
uint256 public rewardPerTokenStored;
uint256 public lastUpdateTime;
uint256 public rewardRate;
mapping(address => UserInfo[]) public userInfo;
event Stake(
address indexed user,
uint256 positionID,
uint256 amount,
uint256 power,
uint256 duration
);
event Withdraw(
address indexed user,
uint256 positionID,
uint256 punish,
uint256 amount,
uint256 power
);
event Harvest(address indexed user, uint256 amount, uint256 positionID);
event SetStakeTimeRatio(uint256[] _stakeTimeRatio);
event SetPunishRate(uint256 punishRate);
event AddStakeTimeRatio(uint256[] _stakeTimeRatio);
event AddNextCycleReward(uint256 rewardAmount);
event SetRewardConfig(uint256 nextCycleReward, uint256 nextDuration);
event StartNewEpoch(uint256 reward, uint256 duration);
constructor(){
setManager(msg.sender,true);
}
function initialize(
address _devAddress,
address _rewardToken,
address _stakedToken,
uint256 _curCycleStartTime,
uint256 _duration,
uint256 _nextCycleReward,
uint256[] memory _stakeTimeRatio
) external onlyManager {
require(!initialized, "initialize: Already initialized!");
require( _stakeTimeRatio.length<=36, "stakeTimeRatio length is invalid!");
withdrawOpened = true;
devAddress = _devAddress;
rewardToken = _rewardToken;
stakedToken = _stakedToken;
curCycleStartTime = _curCycleStartTime - _duration;
periodFinish = _curCycleStartTime;
nextDuration = _duration;
startStakeTime = periodFinish;
nextCycleReward = _nextCycleReward;
stakeTimeRatio = _stakeTimeRatio;
punishRate = 10000;
initialized = true;
}
function notifyMintAmount(uint256 addNextReward) external onlyManager {
uint256 balanceBefore = IERC20(rewardToken).balanceOf(address(this));
IERC20(rewardToken).safeTransferFrom(
msg.sender,
address(this),
addNextReward
);
uint256 balanceEnd = IERC20(rewardToken).balanceOf(address(this));
poolSurplusReward = poolSurplusReward + (balanceEnd - balanceBefore);
emit AddNextCycleReward(poolSurplusReward);
}
function setNextCycleReward(
uint256 _nextCycleReward,
uint256 _nextDuration
) external onlyManager {
nextCycleReward = _nextCycleReward;
nextDuration = _nextDuration;
emit SetRewardConfig(nextCycleReward, nextDuration);
}
function setStakeTimeRatio(
uint256[] memory _stakeTimeRatio
) external onlyManager {
require( _stakeTimeRatio.length<=36, "stakeTimeRatio length is invalid!");
stakeTimeRatio = _stakeTimeRatio;
emit SetStakeTimeRatio(_stakeTimeRatio);
}
function setPunishRate(uint256 _punishRate) external onlyManager {
punishRate = _punishRate;
emit SetPunishRate(_punishRate);
}
function setWithdrawOpened(bool _opened) external onlyManager {
withdrawOpened = _opened;
}
function addStakeTimeRatio(
uint256[] memory _stakeTimeRatio
) external onlyManager {
require(_stakeTimeRatio.length <= 36, "stake time Ratio length is too long");
for (uint256 i = 0; i < _stakeTimeRatio.length; i++) {
stakeTimeRatio.push(_stakeTimeRatio[i]);
}
emit AddStakeTimeRatio(_stakeTimeRatio);
}
modifier checkNextEpoch() {
if (block.timestamp >= periodFinish) {
curCycleReward = nextCycleReward;
require(
poolSurplusReward >= nextCycleReward,
"poolSurplusReward is not enough"
);
poolSurplusReward = poolSurplusReward - nextCycleReward;
curCycleStartTime = block.timestamp;
periodFinish = block.timestamp + (nextDuration);
cycleTimes++;
lastUpdateTime = curCycleStartTime;
rewardRate = curCycleReward / (nextDuration);
totalReward = totalReward + (curCycleReward);
emit StartNewEpoch(curCycleReward, nextDuration);
}
_;
}
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
UserInfo[] storage users = userInfo[account];
for (uint256 i = 0; i < users.length; i++) {
if (users[i].power > 0) {
users[i].reward = earned(account, i);
users[i].rewardPerTokenPaid = rewardPerTokenStored;
}
}
}
_;
}
function rewardPerToken() public view returns (uint256) {
if (totalSupply() == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored +
(((lastTimeRewardApplicable() - lastUpdateTime) *
rewardRate *
1e18) / totalSupply());
}
function stakeForAddress(
uint256 _amount,
uint256 _durationType,
address _stakerAddress
) external updateReward(_stakerAddress) checkNextEpoch onlyManager {
require(_stakerAddress != address(0), "_stakerAddress is empty");
require(block.timestamp >= startStakeTime, "not start");
require(_amount > 0, "Cannot stake 0");
require(_durationType > 0, "stake time is too short");
require(_durationType <= 36, "stake time is too long");
require( _amount > MIN_DEPOSIT_AMOUNT, "Deposit amount must be greater than MIN_DEPOSIT_AMOUNT");
uint256 balanceBefore = IERC20(stakedToken).balanceOf(address(this));
IERC20(stakedToken).safeTransferFrom(
msg.sender,
address(this),
_amount
);
uint256 balanceEnd = IERC20(stakedToken).balanceOf(address(this));
uint256 currentAmount = balanceEnd - balanceBefore;
uint256 stakePower = (currentAmount * (stakeTimeRatio[_durationType])) /
(basRate);
userInfo[_stakerAddress].push(UserInfo(currentAmount, block.timestamp, _durationType, stakePower, 0, 0, rewardPerTokenStored));
uint256 positionID = userInfo[_stakerAddress].length - 1;
totalAmount = totalAmount + (currentAmount);
totalPower = totalPower + (stakePower);
emit Stake(
_stakerAddress,
positionID,
currentAmount,
stakePower,
_durationType
);
}
function stake(
uint256 _amount,
uint256 _durationType
) external updateReward(msg.sender) checkNextEpoch nonReentrant {
require(block.timestamp >= startStakeTime, "not start");
require(_amount > 0, "Cannot stake 0");
require(_durationType > 0, "stake time is too short");
require(_durationType <= 36, "stake time is too long");
require( _amount > MIN_DEPOSIT_AMOUNT, "Deposit amount must be greater than MIN_DEPOSIT_AMOUNT");
uint256 balanceBefore = IERC20(stakedToken).balanceOf(address(this));
IERC20(stakedToken).safeTransferFrom(
msg.sender,
address(this),
_amount
);
uint256 balanceEnd = IERC20(stakedToken).balanceOf(address(this));
uint256 currentAmount = balanceEnd - balanceBefore;
uint256 stakePower = (currentAmount * (stakeTimeRatio[_durationType])) /
(basRate);
userInfo[msg.sender].push(UserInfo(currentAmount, block.timestamp, _durationType, stakePower, 0, 0, rewardPerTokenStored));
uint256 positionID = userInfo[msg.sender].length - 1;
totalAmount = totalAmount + (currentAmount);
totalPower = totalPower + (stakePower);
emit Stake(
msg.sender,
positionID,
currentAmount,
stakePower,
_durationType
);
}
function fixUpdateUserPower(
address user,
uint256 positionID
) external updateReward(user) nonReentrant {
UserInfo storage updateUser = userInfo[user][positionID];
uint256 beforePower = updateUser.power;
uint256 userPower = (updateUser.amount *
(stakeTimeRatio[updateUser.stakeDuration])) / (basRate);
require(userPower != beforePower, "userPower does not change");
updateUser.power = userPower;
totalPower = totalPower - beforePower + (userPower);
}
function withdraw(
uint256 amount,
uint256 positionID
) external updateReward(msg.sender) nonReentrant {
require(
withdrawOpened,
"Have not opened"
);
require(
amount > MIN_WITHDRAW_AMOUNT,
"Withdraw amount must be greater than MIN_WITHDRAW_AMOUNT"
);
UserInfo storage user = userInfo[msg.sender][positionID];
require(user.amount > 0, "no stake amount");
require(user.amount >= amount, "Overdrawing");
uint256 reward = userInfo[msg.sender][positionID].reward;
if (reward > 0) {
user.allReward = user.allReward + (reward);
user.reward = 0;
safeTokenTransfer(msg.sender, reward);
emit Harvest(msg.sender, reward, positionID);
}
uint256 withdrawPower = (amount *
(stakeTimeRatio[user.stakeDuration])) / (basRate);
user.amount = user.amount - amount;
user.power = user.power - withdrawPower;
totalAmount = totalAmount - amount;
totalPower = totalPower - withdrawPower;
uint256 punish = punishStake(msg.sender, amount, positionID);
if (punish > 0) {
IERC20(stakedToken).safeTransfer(devAddress, punish);
}
IERC20(stakedToken).safeTransfer(msg.sender, amount - punish);
emit Withdraw(msg.sender, positionID, punish, amount, withdrawPower);
}
function punishStake(
address user,
uint256 withdrawAmount,
uint256 positionID
) public view returns (uint256) {
UserInfo memory _userInfo = userInfo[user][positionID];
uint256 stakeTime = _userInfo.stakeTime;
uint256 _stakeDuration = _userInfo.stakeDuration;
uint256 shouldDuration = _stakeDuration * monthTime;
uint256 stopStake = stakeTime + shouldDuration;
if (stopStake > block.timestamp) {
uint256 lockTime = block.timestamp - stakeTime;
uint256 punishRatio = (((1e18 -
((lockTime * 1e18) / shouldDuration)) * punishRate) / basRate);
return (punishRatio * withdrawAmount) / 1e18;
} else {
return 0;
}
}
function harvest(uint256 positionID)
external
updateReward(msg.sender)
nonReentrant
{
require(
withdrawOpened,
"Have not opened"
);
uint256 reward = userInfo[msg.sender][positionID].reward;
require(reward > 0, "no reward");
UserInfo storage user = userInfo[msg.sender][positionID];
user.allReward = user.allReward + (reward);
user.reward = 0;
safeTokenTransfer(msg.sender, reward);
emit Harvest(msg.sender, reward, positionID);
}
function lastTimeRewardApplicable() internal view returns (uint256) {
return Math.min(block.timestamp, periodFinish);
}
function earned(address account, uint256 positionID) public view returns (uint256) {
UserInfo memory user = userInfo[account][positionID];
return
(user.power * (rewardPerToken() - (user.rewardPerTokenPaid))) /
(1e18) +
(user.reward);
}
function totalSupply() public view returns (uint256) {
return totalPower;
}
function getUserStakeInfo(
address user,
uint256 positionID
)
external
view
override
returns (
uint256 power,
uint256 amount,
uint256 stakeTime,
uint256 stakeDuration
)
{
UserInfo memory _userInfo = userInfo[user][positionID];
power = _userInfo.power;
amount = _userInfo.amount;
stakeTime = _userInfo.stakeTime;
stakeDuration = _userInfo.stakeDuration;
}
function safeTokenTransfer(address _to, uint256 _amount) internal {
require(rewardToken != address(0x0), "No harvest began");
uint256 tokenBalance = IERC20(rewardToken).balanceOf(address(this));
if (_amount > tokenBalance) {
IERC20(rewardToken).safeTransfer(_to, tokenBalance);
} else {
IERC20(rewardToken).safeTransfer(_to, _amount);
}
}
}
{
"compilationTarget": {
"contracts/staking/UXLINKTokenRewardPoolMultiple.sol": "UXLINKTokenRewardPoolMultiple"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardAmount","type":"uint256"}],"name":"AddNextCycleReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"_stakeTimeRatio","type":"uint256[]"}],"name":"AddStakeTimeRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"Harvest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"punishRate","type":"uint256"}],"name":"SetPunishRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nextCycleReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nextDuration","type":"uint256"}],"name":"SetRewardConfig","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"_stakeTimeRatio","type":"uint256[]"}],"name":"SetStakeTimeRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"power","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"StartNewEpoch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"positionID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"punish","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"power","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MIN_DEPOSIT_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_WITHDRAW_AMOUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_stakeTimeRatio","type":"uint256[]"}],"name":"addStakeTimeRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"basRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curCycleReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"curCycleStartTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cycleTimes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"devAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"fixUpdateUserPower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"getUserStakeInfo","outputs":[{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTime","type":"uint256"},{"internalType":"uint256","name":"stakeDuration","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_devAddress","type":"address"},{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"address","name":"_stakedToken","type":"address"},{"internalType":"uint256","name":"_curCycleStartTime","type":"uint256"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"uint256","name":"_nextCycleReward","type":"uint256"},{"internalType":"uint256[]","name":"_stakeTimeRatio","type":"uint256[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"one","type":"address"}],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"monthTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextCycleReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"addNextReward","type":"uint256"}],"name":"notifyMintAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolSurplusReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"punishRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"punishStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"one","type":"address"},{"internalType":"bool","name":"val","type":"bool"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"list","type":"address[]"},{"internalType":"bool","name":"val","type":"bool"}],"name":"setManagerBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nextCycleReward","type":"uint256"},{"internalType":"uint256","name":"_nextDuration","type":"uint256"}],"name":"setNextCycleReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_punishRate","type":"uint256"}],"name":"setPunishRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_stakeTimeRatio","type":"uint256[]"}],"name":"setStakeTimeRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_opened","type":"bool"}],"name":"setWithdrawOpened","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_durationType","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_durationType","type":"uint256"},{"internalType":"address","name":"_stakerAddress","type":"address"}],"name":"stakeForAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakeTimeRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakedToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startStakeTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakeTime","type":"uint256"},{"internalType":"uint256","name":"stakeDuration","type":"uint256"},{"internalType":"uint256","name":"power","type":"uint256"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"allReward","type":"uint256"},{"internalType":"uint256","name":"rewardPerTokenPaid","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"positionID","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOpened","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]