文件 1 的 19: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 的 19:AccessControlEnumerable.sol
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
return _roleMembers[role].at(index);
}
function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
return _roleMembers[role].length();
}
function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.grantRole(role, account);
_roleMembers[role].add(account);
}
function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.revokeRole(role, account);
_roleMembers[role].remove(account);
}
function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
super.renounceRole(role, account);
_roleMembers[role].remove(account);
}
function _setupRole(bytes32 role, address account) internal virtual override {
super._setupRole(role, account);
_roleMembers[role].add(account);
}
}
文件 3 的 19: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 的 19: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 的 19:ERC721Saver.sol
pragma solidity 0.8.7;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
contract ERC721Saver is AccessControlEnumerable {
bytes32 public constant TOKEN_SAVER_ROLE = keccak256("TOKEN_SAVER_ROLE");
event ERC721Saved(address indexed by, address indexed receiver, address indexed token, uint256 tokenId);
modifier onlyTokenSaver() {
require(hasRole(TOKEN_SAVER_ROLE, _msgSender()), "ERC721Saver.onlyTokenSaver: permission denied");
_;
}
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function saveToken(address _token, address _receiver, uint256 _tokenId) external onlyTokenSaver {
IERC721(_token).safeTransferFrom(address(this), _receiver, _tokenId);
emit ERC721Saved(_msgSender(), _receiver, _token, _tokenId);
}
}
文件 6 的 19:ERC721StakingWithPoint.sol
pragma solidity 0.8.7;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/IStakedERC721.sol";
import "./ERC721Saver.sol";
import "../interfaces/ITimeLockNonTransferablePool.sol";
import "../interfaces/IBasePool.sol";
contract ERC721StakingWithPoint is ReentrancyGuard, ERC721Saver, IBasePool, Ownable {
using Math for uint256;
uint128 public constant POINTS_MULTIPLIER = type(uint128).max;
IERC721 public immutable nft;
IStakedERC721 public immutable stakedNFT;
uint256 public immutable minLockDuration;
uint256 public immutable maxLockDuration;
uint256 public immutable maxBonus;
uint256 public constant MIN_LOCK_DURATION_FOR_SAFETY = 10 minutes;
uint256 public totalStakedPower;
mapping(address => uint256) public userStakedPower;
uint256 public pointsPerShare;
mapping(address => uint256) public withdrawnRewards;
mapping(address => int256) public pointsCorrection;
mapping(uint256 => uint16) public rarityMapping;
IERC20 public immutable rewardToken;
ITimeLockNonTransferablePool public immutable escrowPool;
uint256 public immutable escrowPortion;
uint256 public immutable escrowDuration;
event NFTStaked(address indexed staker, uint256 tokenId, uint256 duration);
event NFTUnstaked(address indexed unstaker, uint256 tokenId);
constructor(
address _nft,
address _stakedNFT,
uint256 _minLockDuration,
uint256 _maxLockDuration,
uint256 _maxBonus,
address _rewardToken,
address _escrowPool,
uint256 _escrowPortion,
uint256 _escrowDuration
) {
require(_nft != address(0), "ERC721StakingWithPoint.constructor: nft cannot be zero address");
require(_stakedNFT != address(0), "ERC721StakingWithPoint.constructor: staked nft cannot be zero address");
require(
_minLockDuration >= MIN_LOCK_DURATION_FOR_SAFETY,
"ERC721StakingWithPoint.constructor: min lock duration must be greater or equal to min lock duration for safety"
);
require(
_maxLockDuration >= _minLockDuration,
"ERC721StakingWithPoint.constructor: max lock duration must be greater or equal to min lock duration"
);
nft = IERC721(_nft);
stakedNFT = IStakedERC721(_stakedNFT);
minLockDuration = _minLockDuration;
maxLockDuration = _maxLockDuration;
maxBonus = _maxBonus;
rewardToken = IERC20(_rewardToken);
escrowPool = ITimeLockNonTransferablePool(_escrowPool);
escrowPortion = _escrowPortion;
escrowDuration = _escrowDuration;
if (_rewardToken != address(0) && _escrowPool != address(0)) {
IERC20(_rewardToken).approve(_escrowPool, type(uint256).max);
}
}
function batchSetRarityMapping(uint256[] memory _tokenIds, uint16[] memory _rarity) external onlyOwner {
require(
_tokenIds.length == _rarity.length,
"ERC721StakingWithPoint.batchSetRarityMapping: tokenIds and rarity length mismatch"
);
for (uint256 i = 0; i < _tokenIds.length; i++) {
rarityMapping[_tokenIds[i]] = _rarity[i];
}
}
function getRarityMultiplier(uint256 _tokenId) public view returns (uint16) {
if (rarityMapping[_tokenId] == 0) {
return 1;
}
return rarityMapping[_tokenId];
}
function stake(uint256 _tokenId, uint256 _duration) external nonReentrant {
_stake(msg.sender, _tokenId, _duration);
}
function _stake(address _staker, uint256 _tokenId, uint256 _duration) internal {
require(nft.ownerOf(_tokenId) == _staker, "ERC721StakingWithPoint.stake: You don't own this token!");
require(block.timestamp + _duration <= type(uint64).max, "ERC721StakingWithPoint.stake: duration too long");
uint256 duration = _duration.min(maxLockDuration);
duration = duration.max(minLockDuration);
nft.transferFrom(_staker, address(this), _tokenId);
stakedNFT.safeMint(
_staker,
_tokenId,
IStakedERC721.StakedInfo({
start: uint64(block.timestamp),
duration: duration,
end: uint64(block.timestamp) + uint64(duration)
})
);
uint256 multiplier = getMultiplier(duration);
uint16 rarityMultiplier = getRarityMultiplier(_tokenId);
totalStakedPower += multiplier * rarityMultiplier;
userStakedPower[_staker] += multiplier * rarityMultiplier;
_correctPoints(_staker, -int256(multiplier * rarityMultiplier));
emit NFTStaked(_staker, _tokenId, _duration);
}
function unstake(uint256 _tokenId) external nonReentrant {
require(stakedNFT.ownerOf(_tokenId) == msg.sender, "ERC721StakingWithPoint.unstake: You don't own this token!");
uint256 multiplier = getMultiplier(stakedNFT.stakedInfoOf(_tokenId).duration);
uint256 rarityMultiplier = getRarityMultiplier(_tokenId);
nft.transferFrom(address(this), msg.sender, _tokenId);
stakedNFT.burn(_tokenId);
totalStakedPower -= multiplier * rarityMultiplier;
userStakedPower[msg.sender] -= multiplier * rarityMultiplier;
_correctPoints(msg.sender, int256(multiplier * rarityMultiplier));
emit NFTUnstaked(msg.sender, _tokenId);
}
function batchStake(uint256[] memory _tokenIds, uint256[] memory _durations) external nonReentrant {
require(
_tokenIds.length == _durations.length,
"ERC721StakingWithPoint.batchStake: tokenIds and durations length mismatch"
);
for (uint256 i = 0; i < _tokenIds.length; i++) {
uint256 tokenId = _tokenIds[i];
uint256 duration = _durations[i];
_stake(msg.sender, tokenId, duration);
}
}
function getMultiplier(uint256 _lockDuration) public view returns (uint256) {
return 1e18 + ((maxBonus * _lockDuration) / maxLockDuration);
}
function distributeRewards(uint256 _amount) external override nonReentrant {
rewardToken.transferFrom(_msgSender(), address(this), _amount);
require(totalStakedPower > 0, "total share supply is zero");
if (_amount > 0) {
pointsPerShare = pointsPerShare + ((_amount * POINTS_MULTIPLIER) / totalStakedPower);
}
}
function claimRewards(address _receiver) external {
uint256 rewardAmount = _prepareCollect(_msgSender());
uint256 escrowedRewardAmount = (rewardAmount * escrowPortion) / 1e18;
uint256 nonEscrowedRewardAmount = rewardAmount - escrowedRewardAmount;
if (escrowedRewardAmount != 0 && address(escrowPool) != address(0)) {
escrowPool.deposit(escrowedRewardAmount, escrowDuration, _receiver);
}
if (nonEscrowedRewardAmount > 1) {
rewardToken.transfer(_receiver, nonEscrowedRewardAmount);
}
}
function _prepareCollect(address _account) internal returns (uint256) {
require(_account != address(0), "AbstractRewards._prepareCollect: account cannot be zero address");
uint256 _withdrawableDividend = withdrawableRewardsOf(_account);
if (_withdrawableDividend > 0) {
withdrawnRewards[_account] = withdrawnRewards[_account] + _withdrawableDividend;
}
return _withdrawableDividend;
}
function withdrawableRewardsOf(address _account) public view returns (uint256) {
return cumulativeRewardsOf(_account) - withdrawnRewards[_account];
}
function withdrawnRewardsOf(address _account) public view returns (uint256) {
return withdrawnRewards[_account];
}
function cumulativeRewardsOf(address _account) public view returns (uint256) {
return
uint256(int256(pointsPerShare * userStakedPower[_account]) + pointsCorrection[_account]) /
POINTS_MULTIPLIER;
}
function _correctPoints(address _account, int256 _shares) internal {
require(_account != address(0), "AbstractRewards._correctPoints: account cannot be zero address");
require(_shares != 0, "AbstractRewards._correctPoints: shares cannot be zero");
pointsCorrection[_account] = pointsCorrection[_account] + (_shares * (int256(pointsPerShare)));
}
}
文件 7 的 19:EnumerableSet.sol
pragma solidity ^0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping(bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = valueIndex;
}
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
文件 8 的 19: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;
}
文件 9 的 19:IAccessControlEnumerable.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
interface IAccessControlEnumerable is IAccessControl {
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
文件 10 的 19:IBasePool.sol
pragma solidity 0.8.7;
interface IBasePool {
function distributeRewards(uint256 _amount) external;
}
文件 11 的 19:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 12 的 19: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);
}
文件 13 的 19:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}
文件 14 的 19:IStakedERC721.sol
pragma solidity 0.8.7;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
interface IStakedERC721 is IERC721 {
function disableTransfer() external;
function enableTransfer() external;
function safeMint(address to, uint256 tokenId, StakedInfo memory stakedInfo) external;
function burn(uint256 tokenId) external;
function stakedInfoOf(uint256 _tokenId) external view returns (StakedInfo memory);
struct StakedInfo {
uint64 start;
uint256 duration;
uint64 end;
}
}
文件 15 的 19:ITimeLockNonTransferablePool.sol
pragma solidity 0.8.7;
interface ITimeLockNonTransferablePool {
function deposit(uint256 _amount, uint256 _duration, address _receiver) external;
}
文件 16 的 19:Math.sol
pragma solidity ^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 & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b + (a % b == 0 ? 0 : 1);
}
}
文件 17 的 19:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_setOwner(_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 {
_setOwner(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 18 的 19: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;
}
}
文件 19 的 19: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/nft/ERC721StakingWithPoint.sol": "ERC721StakingWithPoint"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 100
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_nft","type":"address"},{"internalType":"address","name":"_stakedNFT","type":"address"},{"internalType":"uint256","name":"_minLockDuration","type":"uint256"},{"internalType":"uint256","name":"_maxLockDuration","type":"uint256"},{"internalType":"uint256","name":"_maxBonus","type":"uint256"},{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"address","name":"_escrowPool","type":"address"},{"internalType":"uint256","name":"_escrowPortion","type":"uint256"},{"internalType":"uint256","name":"_escrowDuration","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ERC721Saved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"NFTStaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"unstaker","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"NFTUnstaked","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":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"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_LOCK_DURATION_FOR_SAFETY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POINTS_MULTIPLIER","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_SAVER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"uint16[]","name":"_rarity","type":"uint16[]"}],"name":"batchSetRarityMapping","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_durations","type":"uint256[]"}],"name":"batchStake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"cumulativeRewardsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"distributeRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"escrowDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"escrowPool","outputs":[{"internalType":"contract ITimeLockNonTransferablePool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"escrowPortion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_lockDuration","type":"uint256"}],"name":"getMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"getRarityMultiplier","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"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":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"maxBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minLockDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nft","outputs":[{"internalType":"contract IERC721","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pointsCorrection","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pointsPerShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rarityMapping","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","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":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"saveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedNFT","outputs":[{"internalType":"contract IStakedERC721","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":"totalStakedPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userStakedPower","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"withdrawableRewardsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawnRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"withdrawnRewardsOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]