编译器
0.6.12+commit.27d51765
文件 1 的 11:Address.sol
pragma solidity >=0.6.2 <0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 11:BoringMath.sol
pragma solidity 0.6.12;
library BoringMath {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "BoringMath: division by zero");
return a / b;
}
function to128(uint256 a) internal pure returns (uint128 c) {
require(a <= uint128(-1), "BoringMath: uint128 Overflow");
c = uint128(a);
}
function to64(uint256 a) internal pure returns (uint64 c) {
require(a <= uint64(-1), "BoringMath: uint64 Overflow");
c = uint64(a);
}
function to32(uint256 a) internal pure returns (uint32 c) {
require(a <= uint32(-1), "BoringMath: uint32 Overflow");
c = uint32(a);
}
function to40(uint256 a) internal pure returns (uint40 c) {
require(a <= uint40(-1), "BoringMath: uint40 Overflow");
c = uint40(a);
}
function to112(uint256 a) internal pure returns (uint112 c) {
require(a <= uint112(-1), "BoringMath: uint112 Overflow");
c = uint112(a);
}
function to224(uint256 a) internal pure returns (uint224 c) {
require(a <= uint224(-1), "BoringMath: uint224 Overflow");
c = uint224(a);
}
function to208(uint256 a) internal pure returns (uint208 c) {
require(a <= uint208(-1), "BoringMath: uint208 Overflow");
c = uint208(a);
}
function to216(uint256 a) internal pure returns (uint216 c) {
require(a <= uint216(-1), "BoringMath: uint216 Overflow");
c = uint216(a);
}
}
library BoringMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
library BoringMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
library BoringMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint32 a, uint32 b) internal pure returns (uint32 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function div(uint32 a, uint32 b) internal pure returns (uint32) {
require(b > 0, "BoringMath: division by zero");
return a / b;
}
}
library BoringMath112 {
function add(uint112 a, uint112 b) internal pure returns (uint112 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint112 a, uint112 b) internal pure returns (uint112 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint112 a, uint112 b) internal pure returns (uint112 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function div(uint112 a, uint112 b) internal pure returns (uint112) {
require(b > 0, "BoringMath: division by zero");
return a / b;
}
}
library BoringMath224 {
function add(uint224 a, uint224 b) internal pure returns (uint224 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint224 a, uint224 b) internal pure returns (uint224 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint224 a, uint224 b) internal pure returns (uint224 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function div(uint224 a, uint224 b) internal pure returns (uint224) {
require(b > 0, "BoringMath: division by zero");
return a / b;
}
}
文件 3 的 11:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 4 的 11:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 11:IRewardsEscrow.sol
pragma solidity >0.6.0;
interface IRewardsEscrow {
function lock(
address _address,
uint256 _amount,
uint256 duration
) external;
}
文件 6 的 11:Math.sol
pragma solidity >=0.6.0 <0.8.0;
library Math {
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 / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
文件 7 的 11:Ownable.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 8 的 11:PopLocker.sol
import "openzeppelin-v3/token/ERC20/IERC20.sol";
import "openzeppelin-v3/token/ERC20/SafeERC20.sol";
import "openzeppelin-v3/math/Math.sol";
import "openzeppelin-v3/access/Ownable.sol";
import "openzeppelin-v3/utils/ReentrancyGuard.sol";
import "../libraries/BoringMath.sol";
import "../interfaces/IRewardsEscrow.sol";
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
contract PopLocker is ReentrancyGuard, Ownable {
using BoringMath for uint256;
using BoringMath224 for uint224;
using BoringMath112 for uint112;
using BoringMath32 for uint32;
using SafeERC20 for IERC20;
struct Reward {
bool useBoost;
uint40 periodFinish;
uint208 rewardRate;
uint40 lastUpdateTime;
uint208 rewardPerTokenStored;
}
struct Balances {
uint112 locked;
uint112 boosted;
uint32 nextUnlockIndex;
}
struct LockedBalance {
uint112 amount;
uint112 boosted;
uint32 unlockTime;
}
struct EarnedData {
address token;
uint256 amount;
}
struct Epoch {
uint224 supply;
uint32 date;
}
IERC20 public stakingToken;
IRewardsEscrow public rewardsEscrow;
address[] public rewardTokens;
mapping(address => Reward) public rewardData;
uint256 public escrowDuration;
uint256 public constant rewardsDuration = 7 days;
uint256 public constant lockDuration = rewardsDuration * 12;
mapping(address => mapping(address => bool)) public rewardDistributors;
mapping(address => mapping(address => uint256)) public userRewardPerTokenPaid;
mapping(address => mapping(address => uint256)) public rewards;
uint256 public lockedSupply;
uint256 public boostedSupply;
Epoch[] public epochs;
mapping(address => Balances) public balances;
mapping(address => LockedBalance[]) public userLocks;
address public boostPayment;
uint256 public maximumBoostPayment = 0;
uint256 public boostRate = 10000;
uint256 public nextMaximumBoostPayment = 0;
uint256 public nextBoostRate = 10000;
uint256 public constant denominator = 10000;
uint256 public kickRewardPerEpoch = 100;
uint256 public kickRewardEpochDelay = 4;
bool public isShutdown = false;
string private _name;
string private _symbol;
uint8 private immutable _decimals;
constructor(IERC20 _stakingToken, IRewardsEscrow _rewardsEscrow) public Ownable() {
_name = "Vote Locked POP Token";
_symbol = "vlPOP";
_decimals = 18;
stakingToken = _stakingToken;
rewardsEscrow = _rewardsEscrow;
escrowDuration = 365 days;
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
epochs.push(Epoch({ supply: 0, date: uint32(currentEpoch) }));
}
function decimals() public view returns (uint8) {
return _decimals;
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function addReward(
address _rewardsToken,
address _distributor,
bool _useBoost
) public onlyOwner {
require(rewardData[_rewardsToken].lastUpdateTime == 0);
rewardTokens.push(_rewardsToken);
rewardData[_rewardsToken].lastUpdateTime = uint40(block.timestamp);
rewardData[_rewardsToken].periodFinish = uint40(block.timestamp);
rewardData[_rewardsToken].useBoost = _useBoost;
rewardDistributors[_rewardsToken][_distributor] = true;
}
function approveRewardDistributor(
address _rewardsToken,
address _distributor,
bool _approved
) external onlyOwner {
require(rewardData[_rewardsToken].lastUpdateTime > 0, "rewards token does not exist");
rewardDistributors[_rewardsToken][_distributor] = _approved;
}
function setBoost(
uint256 _max,
uint256 _rate,
address _receivingAddress
) external onlyOwner {
require(_max < 1500, "over max payment");
require(_rate < 30000, "over max rate");
require(_receivingAddress != address(0), "invalid address");
nextMaximumBoostPayment = _max;
nextBoostRate = _rate;
boostPayment = _receivingAddress;
}
function setEscrowDuration(uint256 duration) external onlyOwner {
emit EscrowDurationUpdated(escrowDuration, duration);
escrowDuration = duration;
}
function setKickIncentive(uint256 _rate, uint256 _delay) external onlyOwner {
require(_rate <= 500, "over max rate");
require(_delay >= 2, "min delay");
kickRewardPerEpoch = _rate;
kickRewardEpochDelay = _delay;
}
function shutdown() external onlyOwner {
isShutdown = true;
}
function setApprovals() external {
IERC20(stakingToken).safeApprove(address(rewardsEscrow), 0);
IERC20(stakingToken).safeApprove(address(rewardsEscrow), uint256(-1));
}
function _rewardPerToken(address _rewardsToken) internal view returns (uint256) {
if (boostedSupply == 0) {
return rewardData[_rewardsToken].rewardPerTokenStored;
}
return
uint256(rewardData[_rewardsToken].rewardPerTokenStored).add(
_lastTimeRewardApplicable(rewardData[_rewardsToken].periodFinish)
.sub(rewardData[_rewardsToken].lastUpdateTime)
.mul(rewardData[_rewardsToken].rewardRate)
.mul(1e18)
.div(rewardData[_rewardsToken].useBoost ? boostedSupply : lockedSupply)
);
}
function _earned(
address _user,
address _rewardsToken,
uint256 _balance
) internal view returns (uint256) {
return
_balance.mul(_rewardPerToken(_rewardsToken).sub(userRewardPerTokenPaid[_user][_rewardsToken])).div(1e18).add(
rewards[_user][_rewardsToken]
);
}
function _lastTimeRewardApplicable(uint256 _finishTime) internal view returns (uint256) {
return Math.min(block.timestamp, _finishTime);
}
function lastTimeRewardApplicable(address _rewardsToken) public view returns (uint256) {
return _lastTimeRewardApplicable(rewardData[_rewardsToken].periodFinish);
}
function rewardPerToken(address _rewardsToken) external view returns (uint256) {
return _rewardPerToken(_rewardsToken);
}
function getRewardForDuration(address _rewardsToken) external view returns (uint256) {
return uint256(rewardData[_rewardsToken].rewardRate).mul(rewardsDuration);
}
function claimableRewards(address _account) external view returns (EarnedData[] memory userRewards) {
userRewards = new EarnedData[](rewardTokens.length);
Balances storage userBalance = balances[_account];
uint256 boostedBal = userBalance.boosted;
for (uint256 i = 0; i < userRewards.length; i++) {
address token = rewardTokens[i];
userRewards[i].token = token;
userRewards[i].amount = _earned(_account, token, rewardData[token].useBoost ? boostedBal : userBalance.locked);
}
return userRewards;
}
function rewardWeightOf(address _user) external view returns (uint256 amount) {
return balances[_user].boosted;
}
function lockedBalanceOf(address _user) external view returns (uint256 amount) {
return balances[_user].locked;
}
function balanceOf(address _user) external view returns (uint256 amount) {
LockedBalance[] storage locks = userLocks[_user];
Balances storage userBalance = balances[_user];
uint256 nextUnlockIndex = userBalance.nextUnlockIndex;
amount = balances[_user].boosted;
uint256 locksLength = locks.length;
for (uint256 i = nextUnlockIndex; i < locksLength; i++) {
if (locks[i].unlockTime <= block.timestamp) {
amount = amount.sub(locks[i].boosted);
} else {
break;
}
}
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
if (locksLength > 0 && uint256(locks[locksLength - 1].unlockTime).sub(lockDuration) == currentEpoch) {
amount = amount.sub(locks[locksLength - 1].boosted);
}
return amount;
}
function balanceAtEpochOf(uint256 _epoch, address _user) external view returns (uint256 amount) {
LockedBalance[] storage locks = userLocks[_user];
uint256 epochTime = epochs[_epoch].date;
uint256 cutoffEpoch = epochTime.sub(lockDuration);
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
for (uint256 i = locks.length - 1; i + 1 != 0; i--) {
uint256 lockEpoch = uint256(locks[i].unlockTime).sub(lockDuration);
if (lockEpoch <= epochTime && lockEpoch < currentEpoch) {
if (lockEpoch > cutoffEpoch) {
amount = amount.add(locks[i].boosted);
} else {
break;
}
}
}
return amount;
}
function totalSupply() external view returns (uint256 supply) {
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
uint256 cutoffEpoch = currentEpoch.sub(lockDuration);
uint256 epochindex = epochs.length;
if (uint256(epochs[epochindex - 1].date) == currentEpoch) {
epochindex--;
}
for (uint256 i = epochindex - 1; i + 1 != 0; i--) {
Epoch storage e = epochs[i];
if (uint256(e.date) <= cutoffEpoch) {
break;
}
supply = supply.add(e.supply);
}
return supply;
}
function totalSupplyAtEpoch(uint256 _epoch) external view returns (uint256 supply) {
uint256 epochStart = uint256(epochs[_epoch].date).div(rewardsDuration).mul(rewardsDuration);
uint256 cutoffEpoch = epochStart.sub(lockDuration);
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
if (uint256(epochs[_epoch].date) == currentEpoch) {
_epoch--;
}
for (uint256 i = _epoch; i + 1 != 0; i--) {
Epoch storage e = epochs[i];
if (uint256(e.date) <= cutoffEpoch) {
break;
}
supply = supply.add(epochs[i].supply);
}
return supply;
}
function findEpochId(uint256 _time) external view returns (uint256 epoch) {
uint256 max = epochs.length - 1;
uint256 min = 0;
_time = _time.div(rewardsDuration).mul(rewardsDuration);
for (uint256 i = 0; i < 128; i++) {
if (min >= max) break;
uint256 mid = (min + max + 1) / 2;
uint256 midEpochBlock = epochs[mid].date;
if (midEpochBlock == _time) {
return mid;
} else if (midEpochBlock < _time) {
min = mid;
} else {
max = mid - 1;
}
}
return min;
}
function lockedBalances(address _user)
external
view
returns (
uint256 total,
uint256 unlockable,
uint256 locked,
LockedBalance[] memory lockData
)
{
LockedBalance[] storage locks = userLocks[_user];
Balances storage userBalance = balances[_user];
uint256 nextUnlockIndex = userBalance.nextUnlockIndex;
uint256 idx;
for (uint256 i = nextUnlockIndex; i < locks.length; i++) {
if (locks[i].unlockTime > block.timestamp) {
if (idx == 0) {
lockData = new LockedBalance[](locks.length - i);
}
lockData[idx] = locks[i];
idx++;
locked = locked.add(locks[i].amount);
} else {
unlockable = unlockable.add(locks[i].amount);
}
}
return (userBalance.locked, unlockable, locked, lockData);
}
function epochCount() external view returns (uint256) {
return epochs.length;
}
function checkpointEpoch() external {
_checkpointEpoch();
}
function _checkpointEpoch() internal {
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
uint256 epochindex = epochs.length;
if (epochs[epochindex - 1].date < currentEpoch) {
while (epochs[epochs.length - 1].date != currentEpoch) {
uint256 nextEpochDate = uint256(epochs[epochs.length - 1].date).add(rewardsDuration);
epochs.push(Epoch({ supply: 0, date: uint32(nextEpochDate) }));
}
if (boostRate != nextBoostRate) {
boostRate = nextBoostRate;
}
if (maximumBoostPayment != nextMaximumBoostPayment) {
maximumBoostPayment = nextMaximumBoostPayment;
}
}
}
function lock(
address _account,
uint256 _amount,
uint256 _spendRatio
) external nonReentrant {
stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
_lock(_account, _amount, _spendRatio);
}
function _lock(
address _account,
uint256 _amount,
uint256 _spendRatio
) internal updateReward(_account) {
require(_amount > 0, "Cannot stake 0");
require(_spendRatio <= maximumBoostPayment, "over max spend");
require(!isShutdown, "shutdown");
Balances storage bal = balances[_account];
_checkpointEpoch();
uint256 spendAmount = _amount.mul(_spendRatio).div(denominator);
uint256 boostRatio = boostRate.mul(_spendRatio).div(maximumBoostPayment == 0 ? 1 : maximumBoostPayment);
uint112 lockAmount = _amount.sub(spendAmount).to112();
uint112 boostedAmount = _amount.add(_amount.mul(boostRatio).div(denominator)).to112();
bal.locked = bal.locked.add(lockAmount);
bal.boosted = bal.boosted.add(boostedAmount);
lockedSupply = lockedSupply.add(lockAmount);
boostedSupply = boostedSupply.add(boostedAmount);
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
uint256 unlockTime = currentEpoch.add(lockDuration);
uint256 idx = userLocks[_account].length;
if (idx == 0 || userLocks[_account][idx - 1].unlockTime < unlockTime) {
userLocks[_account].push(
LockedBalance({ amount: lockAmount, boosted: boostedAmount, unlockTime: uint32(unlockTime) })
);
} else {
LockedBalance storage userL = userLocks[_account][idx - 1];
userL.amount = userL.amount.add(lockAmount);
userL.boosted = userL.boosted.add(boostedAmount);
}
Epoch storage e = epochs[epochs.length - 1];
e.supply = e.supply.add(uint224(boostedAmount));
if (spendAmount > 0) {
stakingToken.safeTransfer(boostPayment, spendAmount);
}
emit Staked(_account, _amount, lockAmount, boostedAmount);
}
function _processExpiredLocks(
address _account,
bool _relock,
uint256 _spendRatio,
address _withdrawTo,
address _rewardAddress,
uint256 _checkDelay
) internal updateReward(_account) {
LockedBalance[] storage locks = userLocks[_account];
Balances storage userBalance = balances[_account];
uint112 locked;
uint112 boostedAmount;
uint256 length = locks.length;
uint256 reward = 0;
if (isShutdown || locks[length - 1].unlockTime <= block.timestamp.sub(_checkDelay)) {
locked = userBalance.locked;
boostedAmount = userBalance.boosted;
userBalance.nextUnlockIndex = length.to32();
if (_checkDelay > 0) {
reward = _getDelayAdjustedReward(_checkDelay, locks[length - 1]);
}
} else {
uint32 nextUnlockIndex = userBalance.nextUnlockIndex;
for (uint256 i = nextUnlockIndex; i < length; i++) {
if (locks[i].unlockTime > block.timestamp.sub(_checkDelay)) break;
locked = locked.add(locks[i].amount);
boostedAmount = boostedAmount.add(locks[i].boosted);
if (_checkDelay > 0) {
reward = reward.add(_getDelayAdjustedReward(_checkDelay, locks[i]));
}
nextUnlockIndex++;
}
userBalance.nextUnlockIndex = nextUnlockIndex;
}
require(locked > 0, "no exp locks");
userBalance.locked = userBalance.locked.sub(locked);
userBalance.boosted = userBalance.boosted.sub(boostedAmount);
lockedSupply = lockedSupply.sub(locked);
boostedSupply = boostedSupply.sub(boostedAmount);
if (reward > 0) {
locked = locked.sub(reward.to112());
stakingToken.safeTransfer(_rewardAddress, reward);
emit KickReward(_rewardAddress, _account, reward);
}
if (_relock) {
_lock(_withdrawTo, locked, _spendRatio);
emit Relocked(_account, locked);
} else {
stakingToken.safeTransfer(_withdrawTo, locked);
emit Withdrawn(_account, locked);
}
}
function _getDelayAdjustedReward(uint256 _checkDelay, LockedBalance storage lockedBalance)
internal
view
returns (uint256)
{
uint256 currentEpoch = block.timestamp.sub(_checkDelay).div(rewardsDuration).mul(rewardsDuration);
uint256 epochsover = currentEpoch.sub(uint256(lockedBalance.unlockTime)).div(rewardsDuration);
uint256 rRate = Math.min(kickRewardPerEpoch.mul(epochsover + 1), denominator);
return uint256(lockedBalance.amount).mul(rRate).div(denominator);
}
function processExpiredLocks(
bool _relock,
uint256 _spendRatio,
address _withdrawTo
) external nonReentrant {
_processExpiredLocks(msg.sender, _relock, _spendRatio, _withdrawTo, msg.sender, 0);
}
function processExpiredLocks(bool _relock) external nonReentrant {
_processExpiredLocks(msg.sender, _relock, 0, msg.sender, msg.sender, 0);
}
function kickExpiredLocks(address _account) external nonReentrant {
_processExpiredLocks(_account, false, 0, _account, msg.sender, rewardsDuration.mul(kickRewardEpochDelay));
}
function getReward(address _account) public nonReentrant updateReward(_account) {
for (uint256 i; i < rewardTokens.length; i++) {
address _rewardsToken = rewardTokens[i];
uint256 reward = rewards[_account][_rewardsToken];
if (reward > 0) {
rewards[_account][_rewardsToken] = 0;
uint256 payout = reward.div(uint256(10));
uint256 escrowed = payout.mul(uint256(9));
IERC20(_rewardsToken).safeTransfer(_account, payout);
IRewardsEscrow(rewardsEscrow).lock(_account, escrowed, escrowDuration);
emit RewardPaid(_account, _rewardsToken, reward);
}
}
}
function _notifyReward(address _rewardsToken, uint256 _reward) internal {
Reward storage rdata = rewardData[_rewardsToken];
if (block.timestamp >= rdata.periodFinish) {
rdata.rewardRate = _reward.div(rewardsDuration).to208();
} else {
uint256 remaining = uint256(rdata.periodFinish).sub(block.timestamp);
uint256 leftover = remaining.mul(rdata.rewardRate);
rdata.rewardRate = _reward.add(leftover).div(rewardsDuration).to208();
}
rdata.lastUpdateTime = block.timestamp.to40();
rdata.periodFinish = block.timestamp.add(rewardsDuration).to40();
}
function notifyRewardAmount(address _rewardsToken, uint256 _reward) external updateReward(address(0)) {
require(rewardDistributors[_rewardsToken][msg.sender], "not authorized");
require(_reward > 0, "No reward");
_notifyReward(_rewardsToken, _reward);
IERC20(_rewardsToken).safeTransferFrom(msg.sender, address(this), _reward);
emit RewardAdded(_rewardsToken, _reward);
}
function setRewardsEscrow(address _rewardsEscrow) external onlyOwner {
emit RewardsEscrowUpdated(address(rewardsEscrow), _rewardsEscrow);
rewardsEscrow = IRewardsEscrow(_rewardsEscrow);
}
function recoverERC20(address _tokenAddress, uint256 _tokenAmount) external onlyOwner {
require(_tokenAddress != address(stakingToken), "Cannot withdraw staking token");
require(rewardData[_tokenAddress].lastUpdateTime == 0, "Cannot withdraw reward token");
IERC20(_tokenAddress).safeTransfer(owner(), _tokenAmount);
emit Recovered(_tokenAddress, _tokenAmount);
}
modifier updateReward(address _account) {
{
Balances storage userBalance = balances[_account];
uint256 boostedBal = userBalance.boosted;
for (uint256 i = 0; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
rewardData[token].rewardPerTokenStored = _rewardPerToken(token).to208();
rewardData[token].lastUpdateTime = _lastTimeRewardApplicable(rewardData[token].periodFinish).to40();
if (_account != address(0)) {
rewards[_account][token] = _earned(
_account,
token,
rewardData[token].useBoost ? boostedBal : userBalance.locked
);
userRewardPerTokenPaid[_account][token] = rewardData[token].rewardPerTokenStored;
}
}
}
_;
}
event RewardAdded(address indexed _token, uint256 _reward);
event RewardsEscrowUpdated(address _previous, address _new);
event Staked(address indexed _user, uint256 _paidAmount, uint256 _lockedAmount, uint256 _boostedAmount);
event Withdrawn(address indexed _user, uint256 _amount);
event Relocked(address indexed _user, uint256 _amount);
event EscrowDurationUpdated(uint256 _previousDuration, uint256 _newDuration);
event KickReward(address indexed _user, address indexed _kicked, uint256 _reward);
event RewardPaid(address indexed _user, address indexed _rewardsToken, uint256 _reward);
event Recovered(address _token, uint256 _amount);
}
文件 9 的 11:ReentrancyGuard.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 10 的 11:SafeERC20.sol
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
library SafeERC20 {
using SafeMath for uint256;
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 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 11 的 11:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
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) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
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) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
{
"compilationTarget": {
"contracts/core/dao/PopLocker.sol": "PopLocker"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"_stakingToken","type":"address"},{"internalType":"contract IRewardsEscrow","name":"_rewardsEscrow","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_previousDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newDuration","type":"uint256"}],"name":"EscrowDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"address","name":"_kicked","type":"address"},{"indexed":false,"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"KickReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Relocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"address","name":"_rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_previous","type":"address"},{"indexed":false,"internalType":"address","name":"_new","type":"address"}],"name":"RewardsEscrowUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_paidAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_lockedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_boostedAmount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"address","name":"_distributor","type":"address"},{"internalType":"bool","name":"_useBoost","type":"bool"}],"name":"addReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"address","name":"_distributor","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"approveRewardDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"},{"internalType":"address","name":"_user","type":"address"}],"name":"balanceAtEpochOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint112","name":"locked","type":"uint112"},{"internalType":"uint112","name":"boosted","type":"uint112"},{"internalType":"uint32","name":"nextUnlockIndex","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostPayment","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boostedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkpointEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"claimableRewards","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct PopLocker.EarnedData[]","name":"userRewards","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"denominator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochs","outputs":[{"internalType":"uint224","name":"supply","type":"uint224"},{"internalType":"uint32","name":"date","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"escrowDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_time","type":"uint256"}],"name":"findEpochId","outputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"kickExpiredLocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"kickRewardEpochDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kickRewardPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_spendRatio","type":"uint256"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"lockedBalanceOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"lockedBalances","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"unlockable","type":"uint256"},{"internalType":"uint256","name":"locked","type":"uint256"},{"components":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"boosted","type":"uint112"},{"internalType":"uint32","name":"unlockTime","type":"uint32"}],"internalType":"struct PopLocker.LockedBalance[]","name":"lockData","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maximumBoostPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextBoostRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextMaximumBoostPayment","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_relock","type":"bool"}],"name":"processExpiredLocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_relock","type":"bool"},{"internalType":"uint256","name":"_spendRatio","type":"uint256"},{"internalType":"address","name":"_withdrawTo","type":"address"}],"name":"processExpiredLocks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardData","outputs":[{"internalType":"bool","name":"useBoost","type":"bool"},{"internalType":"uint40","name":"periodFinish","type":"uint40"},{"internalType":"uint208","name":"rewardRate","type":"uint208"},{"internalType":"uint40","name":"lastUpdateTime","type":"uint40"},{"internalType":"uint208","name":"rewardPerTokenStored","type":"uint208"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewardDistributors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsToken","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"rewardWeightOf","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsEscrow","outputs":[{"internalType":"contract IRewardsEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"setApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max","type":"uint256"},{"internalType":"uint256","name":"_rate","type":"uint256"},{"internalType":"address","name":"_receivingAddress","type":"address"}],"name":"setBoost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"setEscrowDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rate","type":"uint256"},{"internalType":"uint256","name":"_delay","type":"uint256"}],"name":"setKickIncentive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsEscrow","type":"address"}],"name":"setRewardsEscrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_epoch","type":"uint256"}],"name":"totalSupplyAtEpoch","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userLocks","outputs":[{"internalType":"uint112","name":"amount","type":"uint112"},{"internalType":"uint112","name":"boosted","type":"uint112"},{"internalType":"uint32","name":"unlockTime","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]