文件 1 的 11:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role, _msgSender());
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role, address account) internal view {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(uint160(account), 20),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) private {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 11:Address.sol
pragma solidity ^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
) internal 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);
}
}
}
}
文件 3 的 11: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;
}
}
文件 4 的 11:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 5 的 11:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 6 的 11:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 7 的 11:IERC20.sol
pragma solidity ^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);
}
文件 8 的 11:PolsStake.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract PolsStake is AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
event Stake(address indexed wallet, uint256 amount, uint256 date);
event Withdraw(address indexed wallet, uint256 amount, uint256 date);
event Claimed(address indexed wallet, address indexed rewardToken, uint256 amount);
event RewardTokenChanged(address indexed oldRewardToken, uint256 returnedAmount, address indexed newRewardToken);
event LockTimePeriodChanged(uint48 lockTimePeriod);
event StakeRewardFactorChanged(uint256 stakeRewardFactor);
event StakeRewardEndTimeChanged(uint48 stakeRewardEndTime);
event RewardsBurned(address indexed staker, uint256 amount);
event ERC20TokensRemoved(address indexed tokenAddress, address indexed receiver, uint256 amount);
uint48 public constant MAX_TIME = type(uint48).max;
struct User {
uint48 stakeTime;
uint48 unlockTime;
uint160 stakeAmount;
uint256 accumulatedRewards;
}
mapping(address => User) public userMap;
uint256 public tokenTotalStaked;
address public immutable stakingToken;
address public rewardToken;
uint48 public lockTimePeriod;
uint48 public stakeRewardEndTime;
uint256 public stakeRewardFactor;
constructor(address _stakingToken, uint48 _lockTimePeriod) {
require(_stakingToken != address(0), "stakingToken.address == 0");
require(_lockTimePeriod < 366 days, "lockTimePeriod >= 366 days");
stakingToken = _stakingToken;
lockTimePeriod = _lockTimePeriod;
stakeRewardFactor = 1000 * 1 days;
stakeRewardEndTime = uint48(block.timestamp + 366 days);
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "value doesn't fit in 48 bits");
return uint48(value);
}
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "value doesn't fit in 160 bits");
return uint160(value);
}
function stakeTime(address _staker) external view returns (uint48 dateTime) {
return userMap[_staker].stakeTime;
}
function stakeAmount(address _staker) external view returns (uint256 balance) {
return userMap[_staker].stakeAmount;
}
function balanceOf(address _staker) external view returns (uint256 balance) {
return userMap[_staker].stakeAmount;
}
function userAccumulatedRewards(address _staker) external view returns (uint256 rewards) {
return userMap[_staker].accumulatedRewards;
}
function getUnlockTime(address _staker) public view returns (uint48 unlockTime) {
return userMap[_staker].stakeAmount > 0 ? userMap[_staker].unlockTime : MAX_TIME;
}
function getRewardTokenBalance() public view returns (uint256 balance) {
if (rewardToken == address(0)) return 0;
balance = IERC20(rewardToken).balanceOf(address(this));
if (stakingToken == rewardToken) {
balance -= tokenTotalStaked;
}
}
function setRewardToken(address newRewardToken) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
address oldRewardToken = rewardToken;
uint256 rewardBalance = getRewardTokenBalance();
if (rewardBalance > 0) {
IERC20(oldRewardToken).safeTransfer(msg.sender, rewardBalance);
}
rewardToken = newRewardToken;
emit RewardTokenChanged(oldRewardToken, rewardBalance, newRewardToken);
}
function setLockTimePeriod(uint48 _lockTimePeriod) external onlyRole(DEFAULT_ADMIN_ROLE) {
lockTimePeriod = _lockTimePeriod;
emit LockTimePeriodChanged(_lockTimePeriod);
}
function setStakeRewardFactor(uint256 _stakeRewardFactor) external onlyRole(DEFAULT_ADMIN_ROLE) {
stakeRewardFactor = _stakeRewardFactor;
emit StakeRewardFactorChanged(_stakeRewardFactor);
}
function setStakeRewardEndTime(uint48 _stakeRewardEndTime) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(stakeRewardEndTime > block.timestamp, "time has to be in the future");
stakeRewardEndTime = _stakeRewardEndTime;
emit StakeRewardEndTimeChanged(_stakeRewardEndTime);
}
function burnRewards(address _staker, uint256 _amount) external onlyRole(BURNER_ROLE) {
User storage user = _updateRewards(_staker);
if (_amount < user.accumulatedRewards) {
user.accumulatedRewards -= _amount;
} else {
user.accumulatedRewards = 0;
}
emit RewardsBurned(_staker, _amount);
}
function stakeAmount_msgSender() public view returns (uint256) {
return userMap[msg.sender].stakeAmount;
}
function stakeTime_msgSender() external view returns (uint48) {
return userMap[msg.sender].stakeTime;
}
function getUnlockTime_msgSender() external view returns (uint48 unlockTime) {
return getUnlockTime(msg.sender);
}
function userClaimableRewards_msgSender() external view returns (uint256) {
return userClaimableRewards(msg.sender);
}
function userAccumulatedRewards_msgSender() external view returns (uint256) {
return userMap[msg.sender].accumulatedRewards;
}
function userTotalRewards_msgSender() external view returns (uint256) {
return userTotalRewards(msg.sender);
}
function getEarnedRewardTokens_msgSender() external view returns (uint256) {
return getEarnedRewardTokens(msg.sender);
}
function userClaimableRewards(address _staker) public view returns (uint256) {
User storage user = userMap[_staker];
if (block.timestamp <= user.stakeTime) return 0;
if (stakeRewardEndTime <= user.stakeTime) return 0;
uint256 timePeriod;
if (block.timestamp <= stakeRewardEndTime) {
timePeriod = block.timestamp - user.stakeTime;
} else {
timePeriod = stakeRewardEndTime - user.stakeTime;
}
return timePeriod * user.stakeAmount;
}
function userTotalRewards(address _staker) public view returns (uint256) {
return userClaimableRewards(_staker) + userMap[_staker].accumulatedRewards;
}
function getEarnedRewardTokens(address _staker) public view returns (uint256 claimableRewardTokens) {
if (address(rewardToken) == address(0) || stakeRewardFactor == 0) {
return 0;
} else {
return userTotalRewards(_staker) / stakeRewardFactor;
}
}
function _updateRewards(address _staker) internal returns (User storage user) {
user = userMap[_staker];
user.accumulatedRewards += userClaimableRewards(_staker);
user.stakeTime = toUint48(block.timestamp);
}
function _stake(uint256 _amount) internal returns (uint256) {
require(_amount > 0, "stake amount must be > 0");
User storage user = _updateRewards(msg.sender);
user.stakeAmount = toUint160(user.stakeAmount + _amount);
tokenTotalStaked += _amount;
user.unlockTime = toUint48(block.timestamp + lockTimePeriod);
IERC20(stakingToken).safeTransferFrom(msg.sender, address(this), _amount);
emit Stake(msg.sender, _amount, toUint48(block.timestamp));
return _amount;
}
function _withdraw(uint256 amount) internal returns (uint256) {
require(amount > 0, "amount to withdraw not > 0");
require(block.timestamp > getUnlockTime(msg.sender), "staked tokens are still locked");
User storage user = _updateRewards(msg.sender);
require(amount <= user.stakeAmount, "withdraw amount > staked amount");
user.stakeAmount -= toUint160(amount);
tokenTotalStaked -= amount;
IERC20(stakingToken).safeTransfer(msg.sender, amount);
emit Withdraw(msg.sender, amount, toUint48(block.timestamp));
return amount;
}
function _claim() internal returns (uint256) {
require(rewardToken != address(0), "no reward token contract");
uint256 earnedRewardTokens = getEarnedRewardTokens(msg.sender);
require(earnedRewardTokens > 0, "no tokens to claim");
User storage user = userMap[msg.sender];
user.accumulatedRewards = 0;
user.stakeTime = toUint48(block.timestamp);
require(earnedRewardTokens <= getRewardTokenBalance(), "not enough reward tokens");
IERC20(rewardToken).safeTransfer(msg.sender, earnedRewardTokens);
emit Claimed(msg.sender, rewardToken, earnedRewardTokens);
return earnedRewardTokens;
}
function stake(uint256 _amount) external nonReentrant returns (uint256) {
return _stake(_amount);
}
function claim() external nonReentrant returns (uint256) {
return _claim();
}
function withdraw(uint256 amount) external nonReentrant returns (uint256) {
return _withdraw(amount);
}
function withdrawAll() external nonReentrant returns (uint256) {
return _withdraw(stakeAmount_msgSender());
}
function removeOtherERC20Tokens(address _tokenAddress) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(_tokenAddress != address(stakingToken), "can not withdraw staking token");
uint256 balance = IERC20(_tokenAddress).balanceOf(address(this));
IERC20(_tokenAddress).safeTransfer(msg.sender, balance);
emit ERC20TokensRemoved(_tokenAddress, msg.sender, balance);
}
}
文件 9 的 11: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() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 10 的 11:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.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 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_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:Strings.sol
pragma solidity ^0.8.0;
library Strings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
function toString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}
{
"compilationTarget": {
"contracts/PolsStake.sol": "PolsStake"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 800
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_stakingToken","type":"address"},{"internalType":"uint48","name":"_lockTimePeriod","type":"uint48"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20TokensRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"lockTimePeriod","type":"uint48"}],"name":"LockTimePeriodChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"returnedAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"newRewardToken","type":"address"}],"name":"RewardTokenChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"date","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"stakeRewardEndTime","type":"uint48"}],"name":"StakeRewardEndTimeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"stakeRewardFactor","type":"uint256"}],"name":"StakeRewardFactorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"date","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"BURNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TIME","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burnRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"getEarnedRewardTokens","outputs":[{"internalType":"uint256","name":"claimableRewardTokens","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getEarnedRewardTokens_msgSender","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokenBalance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"getUnlockTime","outputs":[{"internalType":"uint48","name":"unlockTime","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnlockTime_msgSender","outputs":[{"internalType":"uint48","name":"unlockTime","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockTimePeriod","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"removeOtherERC20Tokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint48","name":"_lockTimePeriod","type":"uint48"}],"name":"setLockTimePeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRewardToken","type":"address"}],"name":"setRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint48","name":"_stakeRewardEndTime","type":"uint48"}],"name":"setStakeRewardEndTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_stakeRewardFactor","type":"uint256"}],"name":"setStakeRewardFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"stakeAmount","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeAmount_msgSender","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeRewardEndTime","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeRewardFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"stakeTime","outputs":[{"internalType":"uint48","name":"dateTime","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakeTime_msgSender","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenTotalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"userAccumulatedRewards","outputs":[{"internalType":"uint256","name":"rewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userAccumulatedRewards_msgSender","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"userClaimableRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userClaimableRewards_msgSender","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userMap","outputs":[{"internalType":"uint48","name":"stakeTime","type":"uint48"},{"internalType":"uint48","name":"unlockTime","type":"uint48"},{"internalType":"uint160","name":"stakeAmount","type":"uint160"},{"internalType":"uint256","name":"accumulatedRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"userTotalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userTotalRewards_msgSender","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]