Contract Overview
My Name Tag:
Not Available
[ Download CSV Export ]
Latest 25 internal transaction
[ Download CSV Export ]
Similar Match Source Code
Note: This contract matches the deployed ByteCode of the Source Code for Contract 0x36B0139A88f7750E402747425EC8B78380db09A0
Contract Name:
NitroPool
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 10 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma abicoder v2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/EnumerableSet.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./interfaces/INFTHandler.sol"; import "./interfaces/INFTPool.sol"; import "./interfaces/INitroPoolFactory.sol"; import "./interfaces/tokens/IGrailTokenV2.sol"; import "./interfaces/tokens/IXGrailToken.sol"; import "./interfaces/INitroCustomReq.sol"; contract NitroPool is ReentrancyGuard, Ownable, INFTHandler { using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; using SafeERC20 for IXGrailToken; using SafeERC20 for IGrailTokenV2; using SafeMath for uint256; struct UserInfo { uint256 totalDepositAmount; // Save total deposit amount uint256 rewardDebtToken1; uint256 rewardDebtToken2; uint256 pendingRewardsToken1; // can't be harvested before harvestStartTime uint256 pendingRewardsToken2; // can't be harvested before harvestStartTime } struct Settings { uint256 startTime; // Start of rewards distribution uint256 endTime; // End of rewards distribution uint256 harvestStartTime; // (optional) Time at which stakers will be allowed to harvest their rewards uint256 depositEndTime; // (optional) Time at which deposits won't be allowed anymore uint256 lockDurationReq; // (optional) required lock duration for positions uint256 lockEndReq; // (optional) required lock end time for positions uint256 depositAmountReq; // (optional) required deposit amount for positions bool whitelist; // (optional) to only allow whitelisted users to deposit string description; // Project's description for this NitroPool } struct RewardsToken { IERC20 token; uint256 amount; // Total rewards to distribute uint256 remainingAmount; // Remaining rewards to distribute uint256 accRewardsPerShare; } struct WhitelistStatus { address account; bool status; } bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; INitroPoolFactory public factory; // NitroPoolFactory address IGrailTokenV2 public grailToken; // GRAILToken contract IXGrailToken public xGrailToken; // xGRAILToken contract INFTPool public nftPool; // NFTPool contract INitroCustomReq public customReqContract; // (optional) external contracts allow to handle custom requirements uint256 public creationTime; // Creation time of this NitroPool bool public published; // Is NitroPool published uint256 public publishTime; // Time at which the NitroPool was published bool public emergencyClose; // When activated, can't distribute rewards anymore RewardsToken public rewardsToken1; // rewardsToken1 data RewardsToken public rewardsToken2; // (optional) rewardsToken2 data // pool info uint256 public totalDepositAmount; uint256 public lastRewardTime; mapping(address => UserInfo) public userInfo; mapping(uint256 => address) public tokenIdOwner; // save tokenId previous owner mapping(address => EnumerableSet.UintSet) private _userTokenIds; // save previous owner tokenIds EnumerableSet.AddressSet private _whitelistedUsers; // whitelisted users Settings public settings; // global and requirements settings constructor( IGrailTokenV2 grailToken_, IXGrailToken xGrailToken_, address owner_, INFTPool nftPool_, IERC20 rewardsToken1_, IERC20 rewardsToken2_, Settings memory settings_ ) { require(address(grailToken_) != address(0) && address(xGrailToken_) != address(0) && owner_ != address(0) && address(nftPool_) != address(0) && address(rewardsToken1_) != address(0), "zero address"); require(_currentBlockTimestamp() < settings_.startTime, "invalid startTime"); require(settings_.startTime < settings_.endTime, "invalid endTime"); require(settings_.depositEndTime == 0 || settings_.startTime <= settings_.depositEndTime, "invalid depositEndTime"); require(settings_.harvestStartTime == 0 || settings_.startTime <= settings_.harvestStartTime, "invalid harvestStartTime"); require(address(rewardsToken1_) != address(rewardsToken2_), "invalid tokens"); factory = INitroPoolFactory(msg.sender); grailToken = grailToken_; xGrailToken = xGrailToken_; nftPool = nftPool_; creationTime = _currentBlockTimestamp(); rewardsToken1.token = rewardsToken1_; rewardsToken2.token = rewardsToken2_; settings.startTime = settings_.startTime; settings.endTime = settings_.endTime; lastRewardTime = settings_.startTime; if (settings_.harvestStartTime == 0) settings.harvestStartTime = settings_.startTime; else settings.harvestStartTime = settings_.harvestStartTime; settings.depositEndTime = settings_.depositEndTime; settings.description = settings_.description; _setRequirements(settings_.lockDurationReq, settings_.lockEndReq, settings_.depositAmountReq, settings_.whitelist); Ownable.transferOwnership(owner_); } /********************************************/ /****************** EVENTS ******************/ /********************************************/ event ActivateEmergencyClose(); event AddRewardsToken1(uint256 amount, uint256 feeAmount); event AddRewardsToken2(uint256 amount, uint256 feeAmount); event Deposit(address indexed userAddress, uint256 tokenId, uint256 amount); event Harvest(address indexed userAddress, IERC20 rewardsToken, uint256 pending); event Publish(); event SetDateSettings(uint256 endTime, uint256 harvestStartTime, uint256 depositEndTime); event SetDescription(string description); event SetRequirements(uint256 lockDurationReq, uint256 lockEndReq, uint256 depositAmountReq, bool whitelist); event SetRewardsToken2(IERC20 rewardsToken2); event SetCustomReqContract(address contractAddress); event UpdatePool(); event WhitelistUpdated(); event Withdraw(address indexed userAddress, uint256 tokenId, uint256 amount); event EmergencyWithdraw(address indexed userAddress, uint256 tokenId, uint256 amount); event WithdrawRewardsToken1(uint256 amount, uint256 totalRewardsAmount); event WithdrawRewardsToken2(uint256 amount, uint256 totalRewardsAmount); /**************************************************/ /****************** PUBLIC VIEWS ******************/ /**************************************************/ /** * @dev Returns the amount of rewardsToken1 distributed every second */ function rewardsToken1PerSecond() public view returns (uint256) { if (settings.endTime <= lastRewardTime) return 0; return rewardsToken1.remainingAmount.div(settings.endTime.sub(lastRewardTime)); } /** * @dev Returns the amount of rewardsToken2 distributed every second */ function rewardsToken2PerSecond() public view returns (uint256) { if (settings.endTime <= lastRewardTime) return 0; return rewardsToken2.remainingAmount.div(settings.endTime.sub(lastRewardTime)); } /** * @dev Returns the number of whitelisted addresses */ function whitelistLength() external view returns (uint256) { return _whitelistedUsers.length(); } /** * @dev Returns a whitelisted address from its "index" */ function whitelistAddress(uint256 index) external view returns (address) { return _whitelistedUsers.at(index); } /** * @dev Checks if "account" address is whitelisted */ function isWhitelisted(address account) external view returns (bool) { return _whitelistedUsers.contains(account); } /** * @dev Returns the number of tokenIds from positions deposited by "account" address */ function userTokenIdsLength(address account) external view returns (uint256) { return _userTokenIds[account].length(); } /** * @dev Returns a position's tokenId deposited by "account" address from its "index" */ function userTokenId(address account, uint256 index) external view returns (uint256) { return _userTokenIds[account].at(index); } /** * @dev Returns pending rewards (rewardsToken1 and rewardsToken2) for "account" address */ function pendingRewards(address account) external view returns (uint256 pending1, uint256 pending2) { UserInfo memory user = userInfo[account]; // recompute accRewardsPerShare for rewardsToken1 & rewardsToken2 if not up to date uint256 accRewardsToken1PerShare_ = rewardsToken1.accRewardsPerShare; uint256 accRewardsToken2PerShare_ = rewardsToken2.accRewardsPerShare; // only if existing deposits and lastRewardTime already passed if (lastRewardTime < _currentBlockTimestamp() && totalDepositAmount > 0) { uint256 rewardsAmount = rewardsToken1PerSecond().mul(_currentBlockTimestamp().sub(lastRewardTime)); // in case of rounding errors if (rewardsAmount > rewardsToken1.remainingAmount) rewardsAmount = rewardsToken1.remainingAmount; accRewardsToken1PerShare_ = accRewardsToken1PerShare_.add(rewardsAmount.mul(1e18).div(totalDepositAmount)); rewardsAmount = rewardsToken2PerSecond().mul(_currentBlockTimestamp().sub(lastRewardTime)); // in case of rounding errors if (rewardsAmount > rewardsToken2.remainingAmount) rewardsAmount = rewardsToken2.remainingAmount; accRewardsToken2PerShare_ = accRewardsToken2PerShare_.add(rewardsAmount.mul(1e18).div(totalDepositAmount)); } pending1 = (user.totalDepositAmount.mul(accRewardsToken1PerShare_).div(1e18).sub(user.rewardDebtToken1)).add(user.pendingRewardsToken1); pending2 = (user.totalDepositAmount.mul(accRewardsToken2PerShare_).div(1e18).sub(user.rewardDebtToken2)).add(user.pendingRewardsToken2); } /***********************************************/ /****************** MODIFIERS ******************/ /***********************************************/ modifier isValidNFTPool(address sender) { require(sender == address(nftPool), "invalid NFTPool"); _; } /*****************************************************************/ /****************** EXTERNAL PUBLIC FUNCTIONS ******************/ /*****************************************************************/ /** * @dev Update this NitroPool */ function updatePool() external nonReentrant { _updatePool(); } /** * @dev Automatically stakes transferred positions from a NFTPool */ function onERC721Received(address /*operator*/, address from, uint256 tokenId, bytes calldata /*data*/) external override nonReentrant isValidNFTPool(msg.sender) returns (bytes4) { require(published, "not published"); require(!settings.whitelist || _whitelistedUsers.contains(from), "not whitelisted"); // save tokenId previous owner _userTokenIds[from].add(tokenId); tokenIdOwner[tokenId] = from; (uint256 amount,uint256 startLockTime, uint256 lockDuration) = _getStackingPosition(tokenId); _checkPositionRequirements(amount, startLockTime, lockDuration); _deposit(from, tokenId, amount); // allow depositor to interact with the staked position later nftPool.approve(from, tokenId); return _ERC721_RECEIVED; } /** * @dev Withdraw a position from the NitroPool * * Can only be called by the position's previous owner */ function withdraw(uint256 tokenId) external virtual nonReentrant { require(msg.sender == tokenIdOwner[tokenId], "not allowed"); (uint256 amount,,) = _getStackingPosition(tokenId); _updatePool(); UserInfo storage user = userInfo[msg.sender]; _harvest(user, msg.sender); user.totalDepositAmount = user.totalDepositAmount.sub(amount); totalDepositAmount = totalDepositAmount.sub(amount); _updateRewardDebt(user); // remove from previous owners info _userTokenIds[msg.sender].remove(tokenId); delete tokenIdOwner[tokenId]; nftPool.safeTransferFrom(address(this), msg.sender, tokenId); emit Withdraw(msg.sender, tokenId, amount); } /** * @dev Withdraw a position from the NitroPool without caring about rewards, EMERGENCY ONLY * * Can only be called by position's previous owner */ function emergencyWithdraw(uint256 tokenId) external virtual nonReentrant { require(msg.sender == tokenIdOwner[tokenId], "not allowed"); (uint256 amount,,) = _getStackingPosition(tokenId); UserInfo storage user = userInfo[msg.sender]; user.totalDepositAmount = user.totalDepositAmount.sub(amount); totalDepositAmount = totalDepositAmount.sub(amount); _updateRewardDebt(user); // remove from previous owners info _userTokenIds[msg.sender].remove(tokenId); delete tokenIdOwner[tokenId]; nftPool.safeTransferFrom(address(this), msg.sender, tokenId); emit EmergencyWithdraw(msg.sender, tokenId, amount); } /** * @dev Harvest pending NitroPool rewards */ function harvest() external nonReentrant { _updatePool(); UserInfo storage user = userInfo[msg.sender]; _harvest(user, msg.sender); _updateRewardDebt(user); } /** * @dev Allow stacked positions to be harvested * * "to" can be set to token's previous owner * "to" can be set to this address only if this contract is allowed to transfer xGRAIL */ function onNFTHarvest(address operator, address to, uint256 tokenId, uint256 grailAmount, uint256 xGrailAmount) external override isValidNFTPool(msg.sender) returns (bool) { address owner = tokenIdOwner[tokenId]; require(operator == owner, "not allowed"); // if not whitelisted, the NitroPool can't transfer any xGRAIL rewards require(to != address(this) || xGrailToken.isTransferWhitelisted(address(this)), "cant handle rewards"); // redirect rewards to position's previous owner if (to == address(this)) { grailToken.safeTransfer(owner, grailAmount); xGrailToken.safeTransfer(owner, xGrailAmount); } return true; } /** * @dev Allow position's previous owner to add more assets to his position */ function onNFTAddToPosition(address operator, uint256 tokenId, uint256 amount) external override nonReentrant isValidNFTPool(msg.sender) returns (bool) { require(operator == tokenIdOwner[tokenId], "not allowed"); _deposit(operator, tokenId, amount); return true; } /** * @dev Disallow withdraw assets from a stacked position */ function onNFTWithdraw(address /*operator*/, uint256 /*tokenId*/, uint256 /*amount*/) external pure override returns (bool){ return false; } /*****************************************************************/ /****************** EXTERNAL OWNABLE FUNCTIONS ******************/ /*****************************************************************/ /** * @dev Transfer ownership of this NitroPool * * Must only be called by the owner of this contract */ function transferOwnership(address newOwner) public override onlyOwner { _setNitroPoolOwner(newOwner); Ownable.transferOwnership(newOwner); } /** * @dev Transfer ownership of this NitroPool * * Must only be called by the owner of this contract */ function renounceOwnership() public override onlyOwner { _setNitroPoolOwner(address(0)); Ownable.renounceOwnership(); } /** * @dev Add rewards to this NitroPool */ function addRewards(uint256 amountToken1, uint256 amountToken2) external nonReentrant { require(_currentBlockTimestamp() < settings.endTime, "pool ended"); _updatePool(); // get active fee share for this NitroPool uint256 feeShare = factory.getNitroPoolFee(address(this), owner()); address feeAddress = factory.feeAddress(); uint256 feeAmount; if (amountToken1 > 0) { // token1 fee feeAmount = amountToken1.mul(feeShare).div(10000); amountToken1 = _transferSupportingFeeOnTransfer(rewardsToken1.token, msg.sender, amountToken1.sub(feeAmount)); // recomputes rewards to distribute rewardsToken1.amount = rewardsToken1.amount.add(amountToken1); rewardsToken1.remainingAmount = rewardsToken1.remainingAmount.add(amountToken1); emit AddRewardsToken1(amountToken1, feeAmount); if (feeAmount > 0) { rewardsToken1.token.safeTransferFrom(msg.sender, feeAddress, feeAmount); } } if (amountToken2 > 0) { require(address(rewardsToken2.token) != address(0), "rewardsToken2"); // token2 fee feeAmount = amountToken2.mul(feeShare).div(10000); amountToken2 = _transferSupportingFeeOnTransfer(rewardsToken2.token, msg.sender, amountToken2.sub(feeAmount)); // recomputes rewards to distribute rewardsToken2.amount = rewardsToken2.amount.add(amountToken2); rewardsToken2.remainingAmount = rewardsToken2.remainingAmount.add(amountToken2); emit AddRewardsToken2(amountToken2, feeAmount); if (feeAmount > 0) { rewardsToken2.token.safeTransferFrom(msg.sender, feeAddress, feeAmount); } } } /** * @dev Withdraw rewards from this NitroPool * * Must only be called by the owner * Must only be called before the publication of the Nitro Pool */ function withdrawRewards(uint256 amountToken1, uint256 amountToken2) external onlyOwner nonReentrant { require(!published, "published"); if (amountToken1 > 0) { // recomputes rewards to distribute rewardsToken1.amount = rewardsToken1.amount.sub(amountToken1, "too high"); rewardsToken1.remainingAmount = rewardsToken1.remainingAmount.sub(amountToken1, "too high"); emit WithdrawRewardsToken1(amountToken1, rewardsToken1.amount); _safeRewardsTransfer(rewardsToken1.token, msg.sender, amountToken1); } if (amountToken2 > 0 && address(rewardsToken2.token) != address(0)) { // recomputes rewards to distribute rewardsToken2.amount = rewardsToken2.amount.sub(amountToken2, "too high"); rewardsToken2.remainingAmount = rewardsToken2.remainingAmount.sub(amountToken2, "too high"); emit WithdrawRewardsToken2(amountToken2, rewardsToken2.amount); _safeRewardsTransfer(rewardsToken2.token, msg.sender, amountToken2); } } /** * @dev Set the rewardsToken2 * * Must only be called by the owner * Must only be initialized once */ function setRewardsToken2(IERC20 rewardsToken2_) external onlyOwner nonReentrant { require(!published, "published"); require(address(rewardsToken2.token) == address(0), "already set"); require(rewardsToken1.token != rewardsToken2_, "invalid"); rewardsToken2.token = rewardsToken2_; emit SetRewardsToken2(rewardsToken2_); } /** * @dev Set an external custom requirement contract */ function setCustomReqContract(address contractAddress) external onlyOwner { // Allow to disable customReq event if pool is published require(!published || contractAddress == address(0), "published"); customReqContract = INitroCustomReq(contractAddress); emit SetCustomReqContract(contractAddress); } /** * @dev Set requirements that positions must meet to be staked on this Nitro Pool * * Must only be called by the owner */ function setRequirements(uint256 lockDurationReq_, uint256 lockEndReq_, uint256 depositAmountReq_, bool whitelist_) external onlyOwner { _setRequirements(lockDurationReq_, lockEndReq_, depositAmountReq_, whitelist_); } /** * @dev Set the pool's datetime settings * * Must only be called by the owner * Nitro duration can only be extended once already published * Harvest start time can only be updated if not published * Deposit end time can only be updated if not been published */ function setDateSettings(uint256 endTime_, uint256 harvestStartTime_, uint256 depositEndTime_) external nonReentrant onlyOwner { require(settings.startTime < endTime_, "invalid endTime"); require(_currentBlockTimestamp() <= settings.endTime, "pool ended"); require(depositEndTime_ == 0 || settings.startTime <= depositEndTime_, "invalid depositEndTime"); require(harvestStartTime_ == 0 || settings.startTime <= harvestStartTime_, "invalid harvestStartTime"); if (published) { // can only be extended require(settings.endTime <= endTime_, "not allowed endTime"); // can't be updated require(settings.depositEndTime == depositEndTime_, "not allowed depositEndTime"); // can't be updated require(settings.harvestStartTime == harvestStartTime_, "not allowed harvestStartTime"); } settings.endTime = endTime_; // updated only when not published if (harvestStartTime_ == 0) settings.harvestStartTime = settings.startTime; else settings.harvestStartTime = harvestStartTime_; settings.depositEndTime = depositEndTime_; emit SetDateSettings(endTime_, harvestStartTime_, depositEndTime_); } /** * @dev Set pool's description * * Must only be called by the owner */ function setDescription(string calldata description) external onlyOwner { settings.description = description; emit SetDescription(description); } /** * @dev Set whitelisted users * * Must only be called by the owner */ function setWhitelist(WhitelistStatus[] calldata whitelistStatuses) external virtual onlyOwner { uint256 whitelistStatusesLength = whitelistStatuses.length; require(whitelistStatusesLength > 0, "empty"); for (uint256 i; i < whitelistStatusesLength; ++i) { if (whitelistStatuses[i].status) _whitelistedUsers.add(whitelistStatuses[i].account); else _whitelistedUsers.remove(whitelistStatuses[i].account); } emit WhitelistUpdated(); } /** * @dev Fully reset the current whitelist * * Must only be called by the owner */ function resetWhitelist() external onlyOwner { uint256 i = _whitelistedUsers.length(); for (i; i > 0; --i) { _whitelistedUsers.remove(_whitelistedUsers.at(i - 1)); } emit WhitelistUpdated(); } /** * @dev Publish the Nitro Pool * * Must only be called by the owner */ function publish() external onlyOwner { require(!published, "published"); // this nitroPool is Stale require(settings.startTime > _currentBlockTimestamp(), "stale"); require(rewardsToken1.amount > 0, "no rewards"); published = true; publishTime = _currentBlockTimestamp(); factory.publishNitroPool(address(nftPool)); emit Publish(); } /** * @dev Emergency close * * Must only be called by the owner * Emergency only: if used, the whole pool is definitely made void * All rewards are automatically transferred to the emergency recovery address */ function activateEmergencyClose() external nonReentrant onlyOwner { address emergencyRecoveryAddress = factory.emergencyRecoveryAddress(); uint256 remainingToken1 = rewardsToken1.remainingAmount; uint256 remainingToken2 = rewardsToken2.remainingAmount; rewardsToken1.amount = rewardsToken1.amount.sub(remainingToken1); rewardsToken1.remainingAmount = 0; rewardsToken2.amount = rewardsToken2.amount.sub(remainingToken2); rewardsToken2.remainingAmount = 0; emergencyClose = true; emit ActivateEmergencyClose(); // transfer rewardsToken1 remaining amount if any _safeRewardsTransfer(rewardsToken1.token, emergencyRecoveryAddress, remainingToken1); // transfer rewardsToken2 remaining amount if any _safeRewardsTransfer(rewardsToken2.token, emergencyRecoveryAddress, remainingToken2); } /********************************************************/ /****************** INTERNAL FUNCTIONS ******************/ /********************************************************/ /** * @dev Set requirements that positions must meet to be staked on this Nitro Pool */ function _setRequirements(uint256 lockDurationReq_, uint256 lockEndReq_, uint256 depositAmountReq_, bool whitelist_) internal { require(lockEndReq_ == 0 || settings.startTime < lockEndReq_, "invalid lockEnd"); if (published) { // Can't decrease requirements if already published require(lockDurationReq_ >= settings.lockDurationReq, "invalid lockDuration"); require(lockEndReq_ >= settings.lockEndReq, "invalid lockEnd"); require(depositAmountReq_ >= settings.depositAmountReq, "invalid depositAmount"); require(!settings.whitelist || settings.whitelist == whitelist_, "invalid whitelist"); } settings.lockDurationReq = lockDurationReq_; settings.lockEndReq = lockEndReq_; settings.depositAmountReq = depositAmountReq_; settings.whitelist = whitelist_; emit SetRequirements(lockDurationReq_, lockEndReq_, depositAmountReq_, whitelist_); } /** * @dev Updates rewards states of this Nitro Pool to be up-to-date */ function _updatePool() internal { uint256 currentBlockTimestamp = _currentBlockTimestamp(); if (currentBlockTimestamp <= lastRewardTime) return; // do nothing if there is no deposit if (totalDepositAmount == 0) { lastRewardTime = currentBlockTimestamp; emit UpdatePool(); return; } // updates rewardsToken1 state uint256 rewardsAmount = rewardsToken1PerSecond().mul(currentBlockTimestamp.sub(lastRewardTime)); // ensure we do not distribute more than what's available if (rewardsAmount > rewardsToken1.remainingAmount) rewardsAmount = rewardsToken1.remainingAmount; rewardsToken1.remainingAmount = rewardsToken1.remainingAmount.sub(rewardsAmount); rewardsToken1.accRewardsPerShare = rewardsToken1.accRewardsPerShare.add(rewardsAmount.mul(1e18).div(totalDepositAmount)); // if rewardsToken2 is activated if (address(rewardsToken2.token) != address(0)) { // updates rewardsToken2 state rewardsAmount = rewardsToken2PerSecond().mul(currentBlockTimestamp.sub(lastRewardTime)); // ensure we do not distribute more than what's available if (rewardsAmount > rewardsToken2.remainingAmount) rewardsAmount = rewardsToken2.remainingAmount; rewardsToken2.remainingAmount = rewardsToken2.remainingAmount.sub(rewardsAmount); rewardsToken2.accRewardsPerShare = rewardsToken2.accRewardsPerShare.add(rewardsAmount.mul(1e18).div(totalDepositAmount)); } lastRewardTime = currentBlockTimestamp; emit UpdatePool(); } /** * @dev Add a user's deposited amount into this Nitro Pool */ function _deposit(address account, uint256 tokenId, uint256 amount) internal { require((settings.depositEndTime == 0 || settings.depositEndTime >= _currentBlockTimestamp()) && !emergencyClose, "not allowed"); if(address(customReqContract) != address(0)){ require(customReqContract.canDeposit(account, tokenId), "invalid customReq"); } _updatePool(); UserInfo storage user = userInfo[account]; _harvest(user, account); user.totalDepositAmount = user.totalDepositAmount.add(amount); totalDepositAmount = totalDepositAmount.add(amount); _updateRewardDebt(user); emit Deposit(account, tokenId, amount); } /** * @dev Transfer to a user its pending rewards */ function _harvest(UserInfo storage user, address to) internal { bool canHarvest = true; if(address(customReqContract) != address(0)){ canHarvest = customReqContract.canHarvest(to); } // rewardsToken1 uint256 pending = user.totalDepositAmount.mul(rewardsToken1.accRewardsPerShare).div(1e18).sub(user.rewardDebtToken1); // check if harvest is allowed if (_currentBlockTimestamp() < settings.harvestStartTime || !canHarvest) { // if not allowed, add to rewards buffer user.pendingRewardsToken1 = user.pendingRewardsToken1.add(pending); } else { // if allowed, transfer rewards pending = pending.add(user.pendingRewardsToken1); user.pendingRewardsToken1 = 0; _safeRewardsTransfer(rewardsToken1.token, to, pending); emit Harvest(to, rewardsToken1.token, pending); } // rewardsToken2 (if initialized) if (address(rewardsToken2.token) != address(0)) { pending = user.totalDepositAmount.mul(rewardsToken2.accRewardsPerShare).div(1e18).sub(user.rewardDebtToken2); // check if harvest is allowed if (_currentBlockTimestamp() < settings.harvestStartTime || !canHarvest) { // if not allowed, add to rewards buffer user.pendingRewardsToken2 = user.pendingRewardsToken2.add(pending); } else { // if allowed, transfer rewards pending = pending.add(user.pendingRewardsToken2); user.pendingRewardsToken2 = 0; _safeRewardsTransfer(rewardsToken2.token, to, pending); emit Harvest(to, rewardsToken2.token, pending); } } } /** * @dev Update a user's rewardDebt for rewardsToken1 and rewardsToken2 */ function _updateRewardDebt(UserInfo storage user) internal virtual { (bool succeed, uint256 result) = user.totalDepositAmount.tryMul(rewardsToken1.accRewardsPerShare); if(succeed) user.rewardDebtToken1 = result.div(1e18); (succeed, result) = user.totalDepositAmount.tryMul(rewardsToken2.accRewardsPerShare); if(succeed) user.rewardDebtToken2 = result.div(1e18); } /** * @dev Check whether a position with "tokenId" ID is meeting all of this Nitro Pool's active requirements */ function _checkPositionRequirements(uint256 amount, uint256 startLockTime, uint256 lockDuration) internal virtual { // lock duration requirement if (settings.lockDurationReq > 0) { // for unlocked position that have not been updated yet require(_currentBlockTimestamp() < startLockTime.add(lockDuration) && settings.lockDurationReq <= lockDuration, "invalid lockDuration"); } // lock end time requirement if (settings.lockEndReq > 0) { require(settings.lockEndReq <= startLockTime.add(lockDuration), "invalid lockEnd"); } // deposit amount requirement if (settings.depositAmountReq > 0) { require(settings.depositAmountReq <= amount, "invalid amount"); } } /** * @dev Handle deposits of tokens with transfer tax */ function _transferSupportingFeeOnTransfer(IERC20 token, address user, uint256 amount) internal returns (uint256 receivedAmount) { uint256 previousBalance = token.balanceOf(address(this)); token.safeTransferFrom(user, address(this), amount); return token.balanceOf(address(this)).sub(previousBalance); } /** * @dev Safe token transfer function, in case rounding error causes pool to not have enough tokens */ function _safeRewardsTransfer(IERC20 token, address to, uint256 amount) internal virtual { if(amount == 0) return; uint256 balance = token.balanceOf(address(this)); // cap to available balance if (amount > balance) { amount = balance; } token.safeTransfer(to, amount); } function _getStackingPosition(uint256 tokenId) internal view returns (uint256 amount, uint256 startLockTime, uint256 lockDuration) { (amount,, startLockTime, lockDuration,,,,) = nftPool.getStakingPosition(tokenId); } function _setNitroPoolOwner(address newOwner) internal { factory.setNitroPoolOwner(owner(), newOwner); } /** * @dev Utility function to get the current block timestamp */ function _currentBlockTimestamp() internal view virtual returns (uint256) { /* solhint-disable not-rely-on-time */ return block.timestamp; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IXGrailToken is IERC20 { function usageAllocations(address userAddress, address usageAddress) external view returns (uint256 allocation); function allocateFromUsage(address userAddress, uint256 amount) external; function convertTo(uint256 amount, address to) external; function deallocateFromUsage(address userAddress, uint256 amount) external; function isTransferWhitelisted(address account) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGrailTokenV2 is IERC20{ function lastEmissionTime() external view returns (uint256); function claimMasterRewards(uint256 amount) external returns (uint256 effectiveAmount); function masterEmissionRate() external view returns (uint256); function burn(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; interface INitroPoolFactory { function emergencyRecoveryAddress() external view returns (address); function feeAddress() external view returns (address); function getNitroPoolFee(address nitroPoolAddress, address ownerAddress) external view returns (uint256); function publishNitroPool(address nftAddress) external; function setNitroPoolOwner(address previousOwner, address newOwner) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; interface INitroCustomReq { function canDepositDescription() external view returns (string calldata); function canHarvestDescription() external view returns (string calldata); function canDeposit(address user, uint256 tokenId) external view returns (bool); function canHarvest(address user) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; interface INFTPool is IERC721 { function exists(uint256 tokenId) external view returns (bool); function hasDeposits() external view returns (bool); function getPoolInfo() external view returns ( address lpToken, address grailToken, address sbtToken, uint256 lastRewardTime, uint256 accRewardsPerShare, uint256 lpSupply, uint256 lpSupplyWithMultiplier, uint256 allocPoint ); function getStakingPosition(uint256 tokenId) external view returns ( uint256 amount, uint256 amountWithMultiplier, uint256 startLockTime, uint256 lockDuration, uint256 lockMultiplier, uint256 rewardDebt, uint256 boostPoints, uint256 totalMultiplier ); function boost(uint256 userAddress, uint256 amount) external; function unboost(uint256 userAddress, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; interface INFTHandler is IERC721Receiver { function onNFTHarvest(address operator, address to, uint256 tokenId, uint256 grailAmount, uint256 xGrailAmount) external returns (bool); function onNFTAddToPosition(address operator, uint256 tokenId, uint256 lpAmount) external returns (bool); function onNFTWithdraw(address operator, uint256 tokenId, uint256 lpAmount) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor () { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (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 { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "../../introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length 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)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } }
{ "remappings": [], "optimizer": { "enabled": true, "runs": 10 }, "evmVersion": "istanbul", "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
[{"inputs":[{"internalType":"contract IGrailTokenV2","name":"grailToken_","type":"address"},{"internalType":"contract IXGrailToken","name":"xGrailToken_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"contract INFTPool","name":"nftPool_","type":"address"},{"internalType":"contract IERC20","name":"rewardsToken1_","type":"address"},{"internalType":"contract IERC20","name":"rewardsToken2_","type":"address"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"harvestStartTime","type":"uint256"},{"internalType":"uint256","name":"depositEndTime","type":"uint256"},{"internalType":"uint256","name":"lockDurationReq","type":"uint256"},{"internalType":"uint256","name":"lockEndReq","type":"uint256"},{"internalType":"uint256","name":"depositAmountReq","type":"uint256"},{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"string","name":"description","type":"string"}],"internalType":"struct NitroPool.Settings","name":"settings_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[],"name":"ActivateEmergencyClose","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"AddRewardsToken1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"AddRewardsToken2","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"contract IERC20","name":"rewardsToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"pending","type":"uint256"}],"name":"Harvest","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":[],"name":"Publish","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"}],"name":"SetCustomReqContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"endTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"harvestStartTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositEndTime","type":"uint256"}],"name":"SetDateSettings","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"description","type":"string"}],"name":"SetDescription","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lockDurationReq","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockEndReq","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositAmountReq","type":"uint256"},{"indexed":false,"internalType":"bool","name":"whitelist","type":"bool"}],"name":"SetRequirements","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"rewardsToken2","type":"address"}],"name":"SetRewardsToken2","type":"event"},{"anonymous":false,"inputs":[],"name":"UpdatePool","type":"event"},{"anonymous":false,"inputs":[],"name":"WhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRewardsAmount","type":"uint256"}],"name":"WithdrawRewardsToken1","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRewardsAmount","type":"uint256"}],"name":"WithdrawRewardsToken2","type":"event"},{"inputs":[],"name":"activateEmergencyClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToken1","type":"uint256"},{"internalType":"uint256","name":"amountToken2","type":"uint256"}],"name":"addRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creationTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"customReqContract","outputs":[{"internalType":"contract INitroCustomReq","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyClose","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"contract INitroPoolFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"grailToken","outputs":[{"internalType":"contract IGrailTokenV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastRewardTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftPool","outputs":[{"internalType":"contract INFTPool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onNFTAddToPosition","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"grailAmount","type":"uint256"},{"internalType":"uint256","name":"xGrailAmount","type":"uint256"}],"name":"onNFTHarvest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"onNFTWithdraw","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"pendingRewards","outputs":[{"internalType":"uint256","name":"pending1","type":"uint256"},{"internalType":"uint256","name":"pending2","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publish","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"publishTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"published","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"resetWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsToken1","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"remainingAmount","type":"uint256"},{"internalType":"uint256","name":"accRewardsPerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken1PerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken2","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"remainingAmount","type":"uint256"},{"internalType":"uint256","name":"accRewardsPerShare","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken2PerSecond","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setCustomReqContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"endTime_","type":"uint256"},{"internalType":"uint256","name":"harvestStartTime_","type":"uint256"},{"internalType":"uint256","name":"depositEndTime_","type":"uint256"}],"name":"setDateSettings","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"description","type":"string"}],"name":"setDescription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lockDurationReq_","type":"uint256"},{"internalType":"uint256","name":"lockEndReq_","type":"uint256"},{"internalType":"uint256","name":"depositAmountReq_","type":"uint256"},{"internalType":"bool","name":"whitelist_","type":"bool"}],"name":"setRequirements","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"rewardsToken2_","type":"address"}],"name":"setRewardsToken2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"internalType":"struct NitroPool.WhitelistStatus[]","name":"whitelistStatuses","type":"tuple[]"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"settings","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"harvestStartTime","type":"uint256"},{"internalType":"uint256","name":"depositEndTime","type":"uint256"},{"internalType":"uint256","name":"lockDurationReq","type":"uint256"},{"internalType":"uint256","name":"lockEndReq","type":"uint256"},{"internalType":"uint256","name":"depositAmountReq","type":"uint256"},{"internalType":"bool","name":"whitelist","type":"bool"},{"internalType":"string","name":"description","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenIdOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDepositAmount","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":[],"name":"updatePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userInfo","outputs":[{"internalType":"uint256","name":"totalDepositAmount","type":"uint256"},{"internalType":"uint256","name":"rewardDebtToken1","type":"uint256"},{"internalType":"uint256","name":"rewardDebtToken2","type":"uint256"},{"internalType":"uint256","name":"pendingRewardsToken1","type":"uint256"},{"internalType":"uint256","name":"pendingRewardsToken2","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"userTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"userTokenIdsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"whitelistAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToken1","type":"uint256"},{"internalType":"uint256","name":"amountToken2","type":"uint256"}],"name":"withdrawRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"xGrailToken","outputs":[{"internalType":"contract IXGrailToken","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b5060405162004cb238038062004cb28339810160408190526200003491620006d0565b60016000908155620000456200031d565b600180546001600160a01b0319166001600160a01b0383169081179091556040519192509060009060008051602062004c92833981519152908290a3506001600160a01b03871615801590620000a357506001600160a01b03861615155b8015620000b857506001600160a01b03851615155b8015620000cd57506001600160a01b03841615155b8015620000e257506001600160a01b03831615155b6200010a5760405162461bcd60e51b815260040162000101906200093a565b60405180910390fd5b80516200011662000321565b10620001365760405162461bcd60e51b8152600401620001019062000825565b60208101518151106200015d5760405162461bcd60e51b8152600401620001019062000997565b606081015115806200017457506060810151815111155b620001935760405162461bcd60e51b81526004016200010190620009c0565b60408101511580620001aa57506040810151815111155b620001c95760405162461bcd60e51b8152600401620001019062000960565b816001600160a01b0316836001600160a01b03161415620001fe5760405162461bcd60e51b8152600401620001019062000879565b60028054336001600160a01b0319918216179091556003805482166001600160a01b038a811691909117909155600480548316898316179055600580549092169086161790556200024e62000321565b600755600b80546001600160a01b038086166001600160a01b031992831617909255600f8054928516929091169190911790558051601a8190556020820151601b556014556040810151620002a8578051601c55620002b1565b6040810151601c555b6060810151601d556101008101518051620002d59160229160209091019062000585565b50620002fa81608001518260a001518360c001518460e001516200032560201b60201c565b62000310856200046b60201b620022ec1760201c565b5050505050505062000a51565b3390565b4290565b821580620003345750601a5483115b620003535760405162461bcd60e51b8152600401620001019062000850565b60085460ff16156200040857601e54841015620003845760405162461bcd60e51b81526004016200010190620008a1565b601f54831015620003a95760405162461bcd60e51b8152600401620001019062000850565b602054821015620003ce5760405162461bcd60e51b81526004016200010190620008d8565b60215460ff161580620003e9575060215460ff161515811515145b620004085760405162461bcd60e51b815260040162000101906200090f565b601e849055601f83905560208290556021805460ff19168215151790556040517f4f49c49ff16acb310d9b9b87703c99594e407985b787e282988a4389faba8bc6906200045d908690869086908690620009f7565b60405180910390a150505050565b620004756200031d565b6001600160a01b03166200048862000576565b6001600160a01b031614620004e4576040805162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015290519081900360640190fd5b6001600160a01b0381166200052b5760405162461bcd60e51b815260040180806020018281038252602681526020018062004c6c6026913960400191505060405180910390fd5b6001546040516001600160a01b0380841692169060008051602062004c9283398151915290600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b031690565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620005bd576000855562000608565b82601f10620005d857805160ff191683800117855562000608565b8280016001018555821562000608579182015b8281111562000608578251825591602001919060010190620005eb565b50620006169291506200061a565b5090565b5b808211156200061657600081556001016200061b565b805180151581146200064257600080fd5b919050565b600082601f83011262000658578081fd5b81516001600160401b038111156200066c57fe5b602062000682601f8301601f1916820162000a14565b828152858284870101111562000696578384fd5b835b83811015620006b557858101830151828201840152820162000698565b83811115620006c657848385840101525b5095945050505050565b600080600080600080600060e0888a031215620006eb578283fd5b8751620006f88162000a38565b60208901519097506200070b8162000a38565b60408901519096506200071e8162000a38565b6060890151909550620007318162000a38565b6080890151909450620007448162000a38565b60a0890151909350620007578162000a38565b60c08901519092506001600160401b038082111562000774578283fd5b818a01915061012080838d0312156200078b578384fd5b620007968162000a14565b9050825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c0820152620007e660e0840162000631565b60e08201526101008084015183811115620007ff578586fd5b6200080d8e82870162000647565b82840152505080935050505092959891949750929550565b602080825260119082015270696e76616c696420737461727454696d6560781b604082015260600190565b6020808252600f908201526e1a5b9d985b1a59081b1bd8dad15b99608a1b604082015260600190565b6020808252600e908201526d696e76616c696420746f6b656e7360901b604082015260600190565b60208082526014908201527f696e76616c6964206c6f636b4475726174696f6e000000000000000000000000604082015260600190565b60208082526015908201527f696e76616c6964206465706f736974416d6f756e740000000000000000000000604082015260600190565b6020808252601190820152701a5b9d985b1a59081dda1a5d195b1a5cdd607a1b604082015260600190565b6020808252600c908201526b7a65726f206164647265737360a01b604082015260600190565b60208082526018908201527f696e76616c69642068617276657374537461727454696d650000000000000000604082015260600190565b6020808252600f908201526e696e76616c696420656e6454696d6560881b604082015260600190565b60208082526016908201527f696e76616c6964206465706f736974456e6454696d6500000000000000000000604082015260600190565b938452602084019290925260408301521515606082015260800190565b6040518181016001600160401b038111828210171562000a3057fe5b604052919050565b6001600160a01b038116811462000a4e57600080fd5b50565b61420b8062000a616000396000f3fe608060405234801561001057600080fd5b506004361061021a5760003560e01c8063075d47821461021f5780630828862d14610229578063150b7a02146102475780631959a0021461026757806321b4921c1461028b578063248a56c0146102ab5780632c2a5928146102b35780632e1a7d4d146102c65780632e3c5919146102d957806331d7a262146102ec57806334677c7a1461030d5780633af32abf1461032d5780634641257d1461034057806346fa466a1461034857806352cb5133146103505780635312ea8e146103635780635909865a14610376578063715018a61461037e57806378bb5164146103865780637c042d611461038e5780638950b9a6146103a15780638d4d2b0c146103b95780638da5cb5b146103c15780638e3291c9146103c957806390c3f38f146103dc5780639231cf74146103ef578063961938f1146103f7578063a9cc9ae91461040a578063ada278ef14610412578063b92ca1971461041a578063bc13c46e14610422578063bf1e33321461042a578063c414c5841461043d578063c45a015514610445578063c5408d501461044d578063c933934a14610455578063d8270dce14610468578063e06174e414610470578063e2b36dc31461048d578063e3161ddd146104a0578063e467fa80146104a8578063f2961711146104b0578063f2fde38b146104c3578063f533c96a146104d6578063fc41aa09146104de578063fe4ee967146104f1575b600080fd5b610227610504565b005b610231610682565b60405161023e9190613a4e565b60405180910390f35b61025a610255366004613731565b610691565b60405161023e9190613ac4565b61027a6102753660046136f9565b610850565b60405161023e959493929190613f7f565b61029e6102993660046136f9565b61087f565b60405161023e9190613f35565b6102276108a6565b6102316102c1366004613936565b610a73565b6102276102d4366004613936565b610a80565b61029e6102e73660046137f1565b610c44565b6102ff6102fa3660046136f9565b610c6d565b60405161023e929190613f3e565b61032061031b36600461381c565b610e18565b60405161023e9190613ab9565b61032061033b3660046136f9565b610e21565b610227610e2e565b610231610ead565b61022761035e366004613966565b610ebc565b610227610371366004613936565b611120565b6102276112c0565b610227611375565b61029e6113eb565b61022761039c366004613850565b6113fd565b6103a9611530565b60405161023e9493929190613ad9565b61032061154b565b610231611554565b6102316103d7366004613936565b611563565b6102276103ea3660046138f7565b61157e565b61029e61162a565b6102276104053660046136f9565b611630565b61032061171d565b61029e611726565b61023161175b565b6103a961176a565b6102276104383660046136f9565b611785565b610231611907565b610231611916565b61029e611925565b6102276104633660046139b2565b61192b565b61029e61199f565b6104786119a5565b60405161023e99989796959493929190613fa2565b61032061049b36600461381c565b611a62565b610227611b30565b61029e611b8a565b6102276104be366004613987565b611b90565b6102276104d13660046136f9565b611db2565b61029e611e29565b6102276104ec366004613966565b611e5e565b6103206104ff3660046137a1565b612188565b61050c6123dd565b6001600160a01b031661051d611554565b6001600160a01b031614610566576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b60085460ff16156105925760405162461bcd60e51b815260040161058990613cb0565b60405180910390fd5b61059a6123e1565b601a54116105ba5760405162461bcd60e51b815260040161058990613b57565b600c546105d95760405162461bcd60e51b815260040161058990613c8c565b6008805460ff191660011790556105ee6123e1565b600955600254600554604051631a3282a560e31b81526001600160a01b039283169263d19415289261062592911690600401613a4e565b600060405180830381600087803b15801561063f57600080fd5b505af1158015610653573d6000803e3d6000fd5b50506040517f12e60d2b86077411e0638198488616eb20137be7d4543ffde74a18c60d840788925060009150a1565b6005546001600160a01b031681565b6000600260005414156106d9576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b600260005560055433906001600160a01b0316811461070a5760405162461bcd60e51b815260040161058990613d93565b60085460ff1661072c5760405162461bcd60e51b815260040161058990613bed565b60215460ff16158061074457506107446018876123e5565b6107605760405162461bcd60e51b815260040161058990613e96565b6001600160a01b038616600090815260176020526040902061078290866123fa565b50600085815260166020526040812080546001600160a01b0319166001600160a01b03891617905580806107b588612406565b9250925092506107c683838361249f565b6107d1898985612546565b60055460405163095ea7b360e01b81526001600160a01b039091169063095ea7b390610803908c908c90600401613aa0565b600060405180830381600087803b15801561081d57600080fd5b505af1158015610831573d6000803e3d6000fd5b5050600160005550630a85bd0160e11b9b9a5050505050505050505050565b601560205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b6001600160a01b03811660009081526017602052604081206108a0906126d2565b92915050565b600260005414156108ec576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b60026000556108f96123dd565b6001600160a01b031661090a611554565b6001600160a01b031614610953576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b60025460408051634633d06160e11b815290516000926001600160a01b031691638c67a0c2916004808301926020929190829003018186803b15801561099857600080fd5b505afa1580156109ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109d09190613715565b600d54601154600c5492935090916109e890836126dd565b600c556000600d556010546109fd90826126dd565b60105560006011819055600a805460ff191660011790556040517f8f2074b85901276b66e93d26866fda1a15b75609ce29ee820a90c3eb1eab2efc9190a1600b54610a52906001600160a01b0316848461273a565b600f54610a69906001600160a01b0316848361273a565b5050600160005550565b60006108a06018836127e5565b60026000541415610ac6576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b60026000908155818152601660205260409020546001600160a01b03163314610b015760405162461bcd60e51b815260040161058990613c14565b6000610b0c82612406565b50509050610b186127f1565b33600081815260156020526040902090610b3390829061292f565b8054610b3f90836126dd565b8155601354610b4e90836126dd565b601355610b5a81612b69565b336000908152601760205260409020610b739084612bd3565b506000838152601660205260409081902080546001600160a01b03191690556005549051632142170760e11b81526001600160a01b03909116906342842e0e90610bc590309033908890600401613a7c565b600060405180830381600087803b158015610bdf57600080fd5b505af1158015610bf3573d6000803e3d6000fd5b50505050336001600160a01b03167ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b5688484604051610c32929190613f3e565b60405180910390a25050600160005550565b6001600160a01b0382166000908152601760205260408120610c6690836127e5565b9392505050565b6001600160a01b0381166000908152601560209081526040808320815160a0810183528154815260018201549381019390935260028101549183019190915260038101546060830152600401546080820152600e5460125483929190610cd16123e1565b601454108015610ce357506000601354115b15610da0576000610d0f610d01601454610cfb6123e1565b906126dd565b610d09611e29565b90612bdf565b600d54909150811115610d215750600d545b601354610d4b90610d4490610d3e84670de0b6b3a7640000612bdf565b90612c38565b8490612c9c565b9250610d66610d5e601454610cfb6123e1565b610d09611726565b601154909150811115610d7857506011545b601354610d9c90610d9590610d3e84670de0b6b3a7640000612bdf565b8390612c9c565b9150505b610dd98360600151610dd38560200151610cfb670de0b6b3a7640000610d3e888a60000151612bdf90919063ffffffff16565b90612c9c565b9450610e0e8360800151610dd38560400151610cfb670de0b6b3a7640000610d3e878a60000151612bdf90919063ffffffff16565b9350505050915091565b60009392505050565b60006108a06018836123e5565b60026000541415610e74576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b6002600055610e816127f1565b33600081815260156020526040902090610e9c90829061292f565b610ea581612b69565b506001600055565b6004546001600160a01b031681565b610ec46123dd565b6001600160a01b0316610ed5611554565b6001600160a01b031614610f1e576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b60026000541415610f64576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b600260005560085460ff1615610f8c5760405162461bcd60e51b815260040161058990613cb0565b8115611045576040805180820190915260088152670e8dede40d0d2ced60c31b6020820152600c54610fbf918490612cf4565b600c556040805180820190915260088152670e8dede40d0d2ced60c31b6020820152600d54610fef918490612cf4565b600d55600c546040517f7ae1cbfc94d111a288e93adca547fd53d15f17df6bca73e865c13805ca7222649161102691859190613f3e565b60405180910390a1600b54611045906001600160a01b0316338461273a565b60008111801561105f5750600f546001600160a01b031615155b15611117576040805180820190915260088152670e8dede40d0d2ced60c31b6020820152601054611091918390612cf4565b6010556040805180820190915260088152670e8dede40d0d2ced60c31b60208201526011546110c1918390612cf4565b6011556010546040517f55638f4a6d3d291dc7d21ee797dd02f3e9b84117ac89d4dd1a066ac015a53b26916110f891849190613f3e565b60405180910390a1600f54611117906001600160a01b0316338361273a565b50506001600055565b60026000541415611166576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b60026000908155818152601660205260409020546001600160a01b031633146111a15760405162461bcd60e51b815260040161058990613c14565b60006111ac82612406565b50503360009081526015602052604090208054919250906111cd90836126dd565b81556013546111dc90836126dd565b6013556111e881612b69565b3360009081526017602052604090206112019084612bd3565b506000838152601660205260409081902080546001600160a01b03191690556005549051632142170760e11b81526001600160a01b03909116906342842e0e9061125390309033908890600401613a7c565b600060405180830381600087803b15801561126d57600080fd5b505af1158015611281573d6000803e3d6000fd5b50505050336001600160a01b03167fbb757047c2b5f3974fe26b7c10f732e7bce710b0952a71082702781e62ae05958484604051610c32929190613f3e565b6112c86123dd565b6001600160a01b03166112d9611554565b6001600160a01b031614611322576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b600061132e60186126d2565b90505b801561135b57611351611349601860001984016127e5565b601890612d8b565b5060001901611331565b60405160008051602061416c83398151915290600090a150565b61137d6123dd565b6001600160a01b031661138e611554565b6001600160a01b0316146113d7576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b6113e16000612da0565b6113e9612e0c565b565b60006113f760186126d2565b90505b90565b6114056123dd565b6001600160a01b0316611416611554565b6001600160a01b03161461145f576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b808061147d5760405162461bcd60e51b815260040161058990613c39565b60005b818110156115135783838281811061149457fe5b90506040020160200160208101906114ac91906138bf565b156114e4576114de8484838181106114c057fe5b6114d692602060409092020190810191506136f9565b601890612ea6565b5061150b565b6115098484838181106114f357fe5b61134992602060409092020190810191506136f9565b505b600101611480565b5060405160008051602061416c83398151915290600090a1505050565b600b54600c54600d54600e546001600160a01b039093169284565b60085460ff1681565b6001546001600160a01b031690565b6016602052600090815260409020546001600160a01b031681565b6115866123dd565b6001600160a01b0316611597611554565b6001600160a01b0316146115e0576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b6115ec60228383613619565b507fab7dca68789bfbe74d09f14600944885d215fdf09987a0aba59a2df718577241828260405161161e929190613aff565b60405180910390a15050565b60145481565b6116386123dd565b6001600160a01b0316611649611554565b6001600160a01b031614611692576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b60085460ff1615806116ab57506001600160a01b038116155b6116c75760405162461bcd60e51b815260040161058990613cb0565b600680546001600160a01b0319166001600160a01b0383161790556040517f070ac22faf9ccb6c368b4dc940445967905c3cd9f50459204e09e30ac382c73290611712908390613a4e565b60405180910390a150565b600a5460ff1681565b6000601454601a600101541161173e575060006113fa565b601454601b546113f79161175291906126dd565b60115490612c38565b6006546001600160a01b031681565b600f546010546011546012546001600160a01b039093169284565b61178d6123dd565b6001600160a01b031661179e611554565b6001600160a01b0316146117e7576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b6002600054141561182d576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b600260005560085460ff16156118555760405162461bcd60e51b815260040161058990613cb0565b600f546001600160a01b03161561187e5760405162461bcd60e51b815260040161058990613eef565b600b546001600160a01b03828116911614156118ac5760405162461bcd60e51b815260040161058990613f14565b600f80546001600160a01b0319166001600160a01b0383161790556040517f5e11eef9026871e4634731ac8981015002d27a183f26034f4f6466e62f0eeb6f906118f7908390613a4e565b60405180910390a1506001600055565b6003546001600160a01b031681565b6002546001600160a01b031681565b60135481565b6119336123dd565b6001600160a01b0316611944611554565b6001600160a01b03161461198d576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b61199984848484612ebb565b50505050565b60075481565b601a8054601b54601c54601d54601e54601f80546020805460215460228054604080516002600019600185161561010002019093169290920497880186900486028201860190528681529a9b999a9899979896979496929560ff9092169493909190830182828015611a585780601f10611a2d57610100808354040283529160200191611a58565b820191906000526020600020905b815481529060010190602001808311611a3b57829003601f168201915b5050505050905089565b600060026000541415611aaa576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b600260005560055433906001600160a01b03168114611adb5760405162461bcd60e51b815260040161058990613d93565b6000848152601660205260409020546001600160a01b03868116911614611b145760405162461bcd60e51b815260040161058990613c14565b611b1f858585612546565b600191505060016000559392505050565b60026000541415611b76576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b6002600055611b836127f1565b6001600055565b60095481565b60026000541415611bd6576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b6002600055611be36123dd565b6001600160a01b0316611bf4611554565b6001600160a01b031614611c3d576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b601a548311611c5e5760405162461bcd60e51b815260040161058990613e6d565b601b54611c696123e1565b1115611c875760405162461bcd60e51b815260040161058990613bc9565b801580611c965750601a548110155b611cb25760405162461bcd60e51b815260040161058990613ebf565b811580611cc15750601a548210155b611cdd5760405162461bcd60e51b815260040161058990613e3b565b60085460ff1615611d4c57601b54831015611d0a5760405162461bcd60e51b815260040161058990613e0e565b601d548114611d2b5760405162461bcd60e51b815260040161058990613c58565b601c548214611d4c5760405162461bcd60e51b815260040161058990613d5d565b601b83905581611d6157601a54601c55611d67565b601c8290555b601d8190556040517f7861cb8c66210f838711e11fe52412c9757ae520575ceee7ee008cbbe57f762290611da090859085908590613f4c565b60405180910390a15050600160005550565b611dba6123dd565b6001600160a01b0316611dcb611554565b6001600160a01b031614611e14576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b611e1d81612da0565b611e26816122ec565b50565b6000601454601a6001015411611e41575060006113fa565b601454601b546113f791611e5591906126dd565b600d5490612c38565b60026000541415611ea4576040805162461bcd60e51b815260206004820152601f602482015260008051602061407f833981519152604482015290519081900360640190fd5b6002600055601b54611eb46123e1565b10611ed15760405162461bcd60e51b815260040161058990613bc9565b611ed96127f1565b6002546000906001600160a01b0316637209736130611ef6611554565b6040518363ffffffff1660e01b8152600401611f13929190613a62565b60206040518083038186803b158015611f2b57600080fd5b505afa158015611f3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f63919061394e565b90506000600260009054906101000a90046001600160a01b03166001600160a01b031663412753586040518163ffffffff1660e01b815260040160206040518083038186803b158015611fb557600080fd5b505afa158015611fc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fed9190613715565b9050600084156120a557612007612710610d3e8786612bdf565b600b5490915061202a906001600160a01b03163361202588856126dd565b612fed565b600c5490955061203a9086612c9c565b600c55600d5461204a9086612c9c565b600d556040517f5066830ee857f8185d6b00ef7557246e98fd7fc2ac0528a4fffe7500533137769061207f9087908490613f3e565b60405180910390a180156120a557600b546120a5906001600160a01b031633848461310c565b831561217c57600f546001600160a01b03166120d35760405162461bcd60e51b815260040161058990613de7565b6120e3612710610d3e8686612bdf565b600f54909150612101906001600160a01b03163361202587856126dd565b6010549094506121119085612c9c565b6010556011546121219085612c9c565b6011556040517f45241e302e41254ac31ac15e7346d8b7e83b49610507828c043e75d97b1ea0d5906121569086908490613f3e565b60405180910390a1801561217c57600f5461217c906001600160a01b031633848461310c565b50506001600055505050565b60055460009033906001600160a01b031681146121b75760405162461bcd60e51b815260040161058990613d93565b6000858152601660205260409020546001600160a01b0390811690881681146121f25760405162461bcd60e51b815260040161058990613c14565b6001600160a01b0387163014158061228357506004805460405162f773f360e51b81526001600160a01b0390911691631eee7e609161223391309101613a4e565b60206040518083038186803b15801561224b57600080fd5b505afa15801561225f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061228391906138db565b61229f5760405162461bcd60e51b815260040161058990613cd3565b6001600160a01b0387163014156122de576003546122c7906001600160a01b03168287613166565b6004546122de906001600160a01b03168286613166565b506001979650505050505050565b6122f46123dd565b6001600160a01b0316612305611554565b6001600160a01b03161461234e576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b6001600160a01b0381166123935760405162461bcd60e51b815260040180806020018281038252602681526020018061409f6026913960400191505060405180910390fd5b6001546040516001600160a01b0380841692169060008051602061414c83398151915290600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b3390565b4290565b6000610c66836001600160a01b0384166131b8565b6000610c6683836131d0565b6005546040516308521f7960e01b8152600091829182916001600160a01b0316906308521f799061243b908790600401613f35565b6101006040518083038186803b15801561245457600080fd5b505afa158015612468573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248c91906139f2565b50959a9399509197509195505050505050565b601e54156124e4576124b18282612c9c565b6124b96123e1565b1080156124c85750601e548110155b6124e45760405162461bcd60e51b815260040161058990613d00565b601f5415612517576124f68282612c9c565b601f5411156125175760405162461bcd60e51b815260040161058990613b2e565b60205415612541576020548310156125415760405162461bcd60e51b815260040161058990613b76565b505050565b601d54158061255e57506125586123e1565b601d5410155b801561256d5750600a5460ff16155b6125895760405162461bcd60e51b815260040161058990613c14565b6006546001600160a01b0316156126385760065460405163387777d760e21b81526001600160a01b039091169063e1dddf5c906125cc9086908690600401613aa0565b60206040518083038186803b1580156125e457600080fd5b505afa1580156125f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061261c91906138db565b6126385760405162461bcd60e51b815260040161058990613b9e565b6126406127f1565b6001600160a01b0383166000908152601560205260409020612662818561292f565b805461266e9083612c9c565b815560135461267d9083612c9c565b60135561268981612b69565b836001600160a01b03167f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1584846040516126c4929190613f3e565b60405180910390a250505050565b60006108a08261321a565b600082821115612734576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b8061274457612541565b6040516370a0823160e01b81526000906001600160a01b038516906370a0823190612773903090600401613a4e565b60206040518083038186803b15801561278b57600080fd5b505afa15801561279f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c3919061394e565b9050808211156127d1578091505b6119996001600160a01b0385168484613166565b6000610c66838361321e565b60006127fb6123e1565b9050601454811161280c57506113e9565b6013546128355760148190556040516000805160206140eb83398151915290600090a1506113e9565b600061284f610d01601454846126dd90919063ffffffff16565b600d549091508111156128615750600d545b600d5461286e90826126dd565b600d556013546128979061288e90610d3e84670de0b6b3a7640000612bdf565b600e5490612c9c565b600e55600f546001600160a01b03161561290f576128c3610d5e601454846126dd90919063ffffffff16565b6011549091508111156128d557506011545b6011546128e290826126dd565b60115560135461290b9061290290610d3e84670de0b6b3a7640000612bdf565b60125490612c9c565b6012555b60148290556040516000805160206140eb83398151915290600090a15050565b6006546001906001600160a01b0316156129c657600654604051637db8130d60e11b81526001600160a01b039091169063fb70261a90612973908590600401613a4e565b60206040518083038186803b15801561298b57600080fd5b505afa15801561299f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129c391906138db565b90505b60006129f88460010154610cfb670de0b6b3a7640000610d3e600b600301548960000154612bdf90919063ffffffff16565b601c54909150612a066123e1565b1080612a10575081155b15612a2e576003840154612a249082612c9c565b6003850155612a99565b6003840154612a3e908290612c9c565b60006003860155600b54909150612a5f906001600160a01b0316848361273a565b600b546040516001600160a01b03858116926000805160206141b683398151915292612a9092909116908590613aa0565b60405180910390a25b600f546001600160a01b0316156119995760028401546012548554612ad19291610cfb91670de0b6b3a764000091610d3e9190612bdf565b601c54909150612adf6123e1565b1080612ae9575081155b15612b07576004840154612afd9082612c9c565b6004850155611999565b6004840154612b17908290612c9c565b60006004860155600f54909150612b38906001600160a01b0316848361273a565b600f546040516001600160a01b03858116926000805160206141b6833981519152926126c492909116908590613aa0565b600e5481546000918291612b7c91613282565b915091508115612b9e57612b9881670de0b6b3a7640000612c38565b60018401555b6012548354612bac91613282565b9092509050811561254157612bc981670de0b6b3a7640000612c38565b6002840155505050565b6000610c6683836132c4565b600082612bee575060006108a0565b82820282848281612bfb57fe5b0414610c665760405162461bcd60e51b815260040180806020018281038252602181526020018061410b6021913960400191505060405180910390fd5b6000808211612c8b576040805162461bcd60e51b815260206004820152601a602482015279536166654d6174683a206469766973696f6e206279207a65726f60301b604482015290519081900360640190fd5b818381612c9457fe5b049392505050565b600082820183811015610c66576040805162461bcd60e51b815260206004820152601b60248201527a536166654d6174683a206164646974696f6e206f766572666c6f7760281b604482015290519081900360640190fd5b60008184841115612d835760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015612d48578181015183820152602001612d30565b50505050905090810190601f168015612d755780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b6000610c66836001600160a01b0384166132c4565b6002546001600160a01b031663df136cfe612db9611554565b836040518363ffffffff1660e01b8152600401612dd7929190613a62565b600060405180830381600087803b158015612df157600080fd5b505af1158015612e05573d6000803e3d6000fd5b5050505050565b612e146123dd565b6001600160a01b0316612e25611554565b6001600160a01b031614612e6e576040805162461bcd60e51b8152602060048201819052602482015260008051602061412c833981519152604482015290519081900360640190fd5b6001546040516000916001600160a01b03169060008051602061414c833981519152908390a3600180546001600160a01b0319169055565b6000610c66836001600160a01b0384166131d0565b821580612ec95750601a5483115b612ee55760405162461bcd60e51b815260040161058990613b2e565b60085460ff1615612f8c57601e54841015612f125760405162461bcd60e51b815260040161058990613d00565b601f54831015612f345760405162461bcd60e51b815260040161058990613b2e565b602054821015612f565760405162461bcd60e51b815260040161058990613d2e565b60215460ff161580612f70575060215460ff161515811515145b612f8c5760405162461bcd60e51b815260040161058990613dbc565b601e849055601f83905560208290556021805460ff19168215151790556040517f4f49c49ff16acb310d9b9b87703c99594e407985b787e282988a4389faba8bc690612fdf908690869086908690613f62565b60405180910390a150505050565b600080846001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161301c9190613a4e565b60206040518083038186803b15801561303457600080fd5b505afa158015613048573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061306c919061394e565b90506130836001600160a01b03861685308661310c565b61310381866001600160a01b03166370a08231306040518263ffffffff1660e01b81526004016130b39190613a4e565b60206040518083038186803b1580156130cb57600080fd5b505afa1580156130df573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfb919061394e565b95945050505050565b604080516001600160a01b0380861660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261199990859061338a565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261254190849061338a565b60009081526001919091016020526040902054151590565b60006131dc83836131b8565b613212575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556108a0565b5060006108a0565b5490565b815460009082106132605760405162461bcd60e51b815260040180806020018281038252602281526020018061405d6022913960400191505060405180910390fd5b82600001828154811061326f57fe5b9060005260206000200154905092915050565b6000808361329657506001905060006132bd565b838302838582816132a357fe5b04146132b65760008092509250506132bd565b6001925090505b9250929050565b6000818152600183016020526040812054801561338057835460001980830191908101906000908790839081106132f757fe5b906000526020600020015490508087600001848154811061331457fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061334457fe5b600190038181906000526020600020016000905590558660010160008781526020019081526020016000206000905560019450505050506108a0565b60009150506108a0565b60006133df826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661343b9092919063ffffffff16565b805190915015612541578080602001905160208110156133fe57600080fd5b50516125415760405162461bcd60e51b815260040180806020018281038252602a81526020018061418c602a913960400191505060405180910390fd5b606061344a8484600085613452565b949350505050565b6060824710156134935760405162461bcd60e51b81526004018080602001828103825260268152602001806140c56026913960400191505060405180910390fd5b61349c856135ad565b6134ed576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b6020831061352b5780518252601f19909201916020918201910161350c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461358d576040519150601f19603f3d011682016040523d82523d6000602084013e613592565b606091505b50915091506135a28282866135b3565b979650505050505050565b3b151590565b606083156135c2575081610c66565b8251156135d25782518084602001fd5b60405162461bcd60e51b8152602060048201818152845160248401528451859391928392604401919085019080838360008315612d48578181015183820152602001612d30565b828054600181600116156101000203166002900490600052602060002090601f01602090048101928261364f5760008555613695565b82601f106136685782800160ff19823516178555613695565b82800160010185558215613695579182015b8281111561369557823582559160200191906001019061367a565b506136a19291506136a5565b5090565b5b808211156136a157600081556001016136a6565b60008083601f8401126136cb578182fd5b5081356001600160401b038111156136e1578182fd5b6020830191508360208285010111156132bd57600080fd5b60006020828403121561370a578081fd5b8135610c6681614039565b600060208284031215613726578081fd5b8151610c6681614039565b600080600080600060808688031215613748578081fd5b853561375381614039565b9450602086013561376381614039565b93506040860135925060608601356001600160401b03811115613784578182fd5b613790888289016136ba565b969995985093965092949392505050565b600080600080600060a086880312156137b8578081fd5b85356137c381614039565b945060208601356137d381614039565b94979496505050506040830135926060810135926080909101359150565b60008060408385031215613803578182fd5b823561380e81614039565b946020939093013593505050565b600080600060608486031215613830578283fd5b833561383b81614039565b95602085013595506040909401359392505050565b60008060208385031215613862578182fd5b82356001600160401b0380821115613878578384fd5b818501915085601f83011261388b578384fd5b813581811115613899578485fd5b8660206040830285010111156138ad578485fd5b60209290920196919550909350505050565b6000602082840312156138d0578081fd5b8135610c668161404e565b6000602082840312156138ec578081fd5b8151610c668161404e565b60008060208385031215613909578182fd5b82356001600160401b0381111561391e578283fd5b61392a858286016136ba565b90969095509350505050565b600060208284031215613947578081fd5b5035919050565b60006020828403121561395f578081fd5b5051919050565b60008060408385031215613978578081fd5b50508035926020909101359150565b60008060006060848603121561399b578081fd5b505081359360208301359350604090920135919050565b600080600080608085870312156139c7578182fd5b84359350602085013592506040850135915060608501356139e78161404e565b939692955090935050565b600080600080600080600080610100898b031215613a0e578586fd5b505086516020880151604089015160608a015160808b015160a08c015160c08d015160e0909d0151959e949d50929b919a50985090965094509092509050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b6001600160e01b031991909116815260200190565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b60006020825282602083015282846040840137818301604090810191909152601f909201601f19160101919050565b6020808252600f908201526e1a5b9d985b1a59081b1bd8dad15b99608a1b604082015260600190565b6020808252600590820152647374616c6560d81b604082015260600190565b6020808252600e908201526d1a5b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b602080825260119082015270696e76616c696420637573746f6d52657160781b604082015260600190565b6020808252600a90820152691c1bdbdb08195b99195960b21b604082015260600190565b6020808252600d908201526c1b9bdd081c1d589b1a5cda1959609a1b604082015260600190565b6020808252600b908201526a1b9bdd08185b1b1bddd95960aa1b604082015260600190565b602080825260059082015264656d70747960d81b604082015260600190565b6020808252601a90820152796e6f7420616c6c6f776564206465706f736974456e6454696d6560301b604082015260600190565b6020808252600a90820152696e6f207265776172647360b01b604082015260600190565b6020808252600990820152681c1d589b1a5cda195960ba1b604082015260600190565b60208082526013908201527263616e742068616e646c65207265776172647360681b604082015260600190565b60208082526014908201527334b73b30b634b2103637b1b5a23ab930ba34b7b760611b604082015260600190565b6020808252601590820152741a5b9d985b1a590819195c1bdcda5d105b5bdd5b9d605a1b604082015260600190565b6020808252601c908201527b6e6f7420616c6c6f7765642068617276657374537461727454696d6560201b604082015260600190565b6020808252600f908201526e1a5b9d985b1a5908139195141bdbdb608a1b604082015260600190565b6020808252601190820152701a5b9d985b1a59081dda1a5d195b1a5cdd607a1b604082015260600190565b6020808252600d908201526c3932bbb0b93239aa37b5b2b71960991b604082015260600190565b6020808252601390820152726e6f7420616c6c6f77656420656e6454696d6560681b604082015260600190565b602080825260189082015277696e76616c69642068617276657374537461727454696d6560401b604082015260600190565b6020808252600f908201526e696e76616c696420656e6454696d6560881b604082015260600190565b6020808252600f908201526e1b9bdd081dda1a5d195b1a5cdd1959608a1b604082015260600190565b602080825260169082015275696e76616c6964206465706f736974456e6454696d6560501b604082015260600190565b6020808252600b908201526a185b1c9958591e481cd95d60aa1b604082015260600190565b6020808252600790820152661a5b9d985b1a5960ca1b604082015260600190565b90815260200190565b918252602082015260400190565b9283526020830191909152604082015260600190565b938452602084019290925260408301521515606082015260800190565b948552602085019390935260408401919091526060830152608082015260a00190565b60006101208b835260208b818501528a60408501528960608501528860808501528760a08501528660c085015285151560e085015281610100850152845180838601528392505b8083101561400857858301820151858401610140015291810191613fe9565b8083111561401a578361014082870101525b601f01601f191693909301610140019c9b505050505050505050505050565b6001600160a01b0381168114611e2657600080fd5b8015158114611e2657600080fdfe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e64735265656e7472616e637947756172643a207265656e7472616e742063616c6c004f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c3b995fb49557022d683e66ab27e0339ab7db40111da9b4f57765b99e20f78686536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65728be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e022f196d9cf5ab2abfb5e4f2e291a2452f5c28d88e99b62b74b9c6e62ab231a445361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a0306f61d3fafe13787b78e276cb6b644382854a66cb46daae14227d3ec26797a264697066735822122025f1bf719c667b3379a6e8ba3f64331b4fb74eff1538942cf1c7acf2c7c4c2ae64736f6c634300070600334f776e61626c653a206e6577206f776e657220697320746865207a65726f20616464726573738be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e00000000000000000000000003d9907f9a368ad0a51be60f7da3b97cf940982d80000000000000000000000003caae25ee616f2c8e13c74da0813402eae3f496b00000000000000000000000001bb7b44cc398aaa2b76ac6253f0f5634279db9d0000000000000000000000006bc938aba940fb828d39daa23a94dfc522120c110000000000000000000000003caae25ee616f2c8e13c74da0813402eae3f496b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000637e438000000000000000000000000000000000000000000000000000000000646ce300000000000000000000000000000000000000000000000000000000006390c69000000000000000000000000000000000000000000000000000000000638a2f10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.