Source Code
Overview
GLMR Balance
GLMR Value
$0.00Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 14176919 | 4 days ago | 0 GLMR | ||||
| 14087921 | 11 days ago | 0 GLMR | ||||
| 14001410 | 18 days ago | 0 GLMR | ||||
| 13916023 | 25 days ago | 0 GLMR | ||||
| 13830382 | 32 days ago | 0 GLMR | ||||
| 13743367 | 39 days ago | 0 GLMR | ||||
| 13664435 | 45 days ago | 0 GLMR | ||||
| 13575115 | 53 days ago | 0 GLMR | ||||
| 13492383 | 60 days ago | 0 GLMR | ||||
| 13408854 | 67 days ago | 0 GLMR | ||||
| 13322760 | 74 days ago | 0 GLMR | ||||
| 13237270 | 81 days ago | 0 GLMR | ||||
| 13149192 | 88 days ago | 0 GLMR | ||||
| 13065023 | 95 days ago | 0 GLMR | ||||
| 12980343 | 102 days ago | 0 GLMR | ||||
| 12895484 | 109 days ago | 0 GLMR | ||||
| 12809808 | 116 days ago | 0 GLMR | ||||
| 12724985 | 123 days ago | 0 GLMR | ||||
| 12636011 | 130 days ago | 0 GLMR | ||||
| 12544347 | 137 days ago | 0 GLMR | ||||
| 12449142 | 144 days ago | 0 GLMR | ||||
| 12351912 | 151 days ago | 0 GLMR | ||||
| 12348640 | 151 days ago | 0 GLMR | ||||
| 12253741 | 158 days ago | 0 GLMR | ||||
| 12155231 | 165 days ago | 0 GLMR |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Voting
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {VotingErrors} from "./libraries/VotingErrors.sol";
import {IVEStella} from "./interfaces/IVEStella.sol";
import {IIncentiveManager} from "./interfaces/IIncentiveManager.sol";
import {IRewarder} from "./interfaces/IRewarder.sol";
import {IIncentiveManagerFactory} from "./interfaces/IIncentiveManagerFactory.sol";
import {IMinter} from "./interfaces/IMinter.sol";
import {IRewardRegistry} from "./interfaces/IRewardRegistry.sol";
import {IncentiveManager} from "./rewards/IncentiveManager.sol";
import {IWGLMR} from "./interfaces/IWGLMR.sol";
import {TimeLibrary} from "./libraries/Timelibrary.sol";
contract Voting is ReentrancyGuard, AccessControl {
using SafeERC20 for IERC20;
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant EMERGENCY_COUNCIL_ROLE = keccak256("EMERGENCY_COUNCIL_ROLE");
IVEStella public veStellaToken;
uint256 public MAX_VOTES_PER_EPOCH = 30;
uint256 public totalVotes; // Total votes across all pools
uint256 public poolCount; // Total number of pools
uint256 public epochDuration = 1 weeks;
uint256 public lastEpochTime;
uint256 public COOL_DOWN_PERIOD = 1 hours;
uint256 public processedPools;
uint256 public startOfVoteEpoch = 1751500800;
address public wrappedGLMR;
address public rewardRegistryAddress;
address public incentiveManagerFactory;
address public minter;
bool public emergencyPaused = false;
struct Pool {
uint256 totalVotes;
bool isRegistered;
address feeDistributor;
address bribeDistributor;
}
struct VoteCheckpoint {
uint256 totalVotes; // Total votes in a particular checkpoint (epoch)
mapping(address => uint256) poolVotes;
uint256 epoch;
bool isVoting;
address[] pools;
}
mapping(address => Pool) public pools; // poolAddress => Pool
mapping(address => bool) public isAlive;
mapping(uint256 => VoteCheckpoint) public nftVoteData; // Nft id to voteCHeckpoint
// for distribute rewards
mapping(uint256 => bool) public epochRewardDistributed;
address[] public poolAddresses;
address[] public rewardTokens;
mapping(address => bool) public isRewardToken; // Tracking if a token is in use
mapping(address => uint256) public epochRewards; // Reward allocated per epoch per token
mapping(address => bool) public bribeWhiteListedRewardToken;
address[] public bribeWhiteListedRewardTokenList;
event VoteCast(uint256 indexed voterNft, address[] poolIds, uint256[] weights);
event RewardsDistributed(uint256 epoch);
event RewardAllocated(address poolId, address token, uint256 reward);
event RewardNotified(address[] tokens, uint256[] amounts);
event AddWhitelistedBribeToken(address token);
event RemoveWhitelistedBribeToken(address token);
event RewardTokenAdded(address token);
event RewardTokenRemoved(address token);
event PoolRegistered(address poolAddress, address feeDistributor, address bribe);
event PoolKilled(address poolAddress);
event PoolRevived(address poolAddress);
event MaxVotesPerEpochUpdated(uint256 _perEpoch);
event CoolDownPeriodUpdated(uint256 _newCoolDownPeriod);
modifier onlyDuringEpoch(uint256 _nftId) {
uint256 currentEpoch = getCurrentEpoch();
VoteCheckpoint storage voteData = nftVoteData[_nftId];
if (voteData.epoch >= currentEpoch) {
revert VotingErrors.AlreadyVoted();
}
// uint256 nextEpochStartTime = (lastEpochTime + 1) * epochDuration;
// if (
// block.timestamp >= nextEpochStartTime - COOL_DOWN_PERIOD
// && block.timestamp <= nextEpochStartTime + COOL_DOWN_PERIOD
// )
if (
block.timestamp <= TimeLibrary.epochStart(block.timestamp) + COOL_DOWN_PERIOD
|| block.timestamp >= TimeLibrary.epochNext(block.timestamp) - COOL_DOWN_PERIOD
) {
revert VotingErrors.VotingInCoolDown();
}
_;
}
modifier ownerOrEscrow(uint256 _tokenId) {
if (!veStellaToken.isApprovedOrOwner(msg.sender, _tokenId)) {
revert VotingErrors.OnlyOwnerOrEscrow();
}
_;
}
modifier whenNotPaused() {
require(!emergencyPaused, "Contract paused");
_;
}
constructor(
address _veStellaToken,
address _WGLMR,
address _rewardRegistryAddress,
address _incentiveManagerFactory,
address _minter
) {
veStellaToken = IVEStella(_veStellaToken);
lastEpochTime = getCurrentEpoch();
wrappedGLMR = _WGLMR;
rewardRegistryAddress = _rewardRegistryAddress;
incentiveManagerFactory = _incentiveManagerFactory;
minter = _minter;
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(EMERGENCY_COUNCIL_ROLE, msg.sender);
_setupRole(ADMIN_ROLE, msg.sender);
}
function setIncentiveManagerFactory(address _incentiveManagerFactory) external onlyRole(ADMIN_ROLE) {
incentiveManagerFactory = _incentiveManagerFactory;
}
function togglePause(bool _toggle) external onlyRole(ADMIN_ROLE) {
emergencyPaused = _toggle;
}
function addRewardToken(address _token) external onlyRole(ADMIN_ROLE) {
if (isRewardToken[_token]) {
revert VotingErrors.TokenAlreadyAdded();
}
rewardTokens.push(_token);
isRewardToken[_token] = true;
emit RewardTokenAdded(_token);
}
function setStartOfVoteEpoch(uint256 _startOfVoteEpoch) external onlyRole(ADMIN_ROLE) {
startOfVoteEpoch = _startOfVoteEpoch;
}
function addWhitelistedBribeToken(address _token) external onlyRole(ADMIN_ROLE) {
if (bribeWhiteListedRewardToken[_token]) {
revert VotingErrors.TokenAlreadyAdded();
}
bribeWhiteListedRewardToken[_token] = true;
bribeWhiteListedRewardTokenList.push(_token);
emit AddWhitelistedBribeToken(_token);
}
function removeWhitelistedBribeToken(address _token) external onlyRole(ADMIN_ROLE) {
if (!bribeWhiteListedRewardToken[_token]) {
revert VotingErrors.TokenNotInList();
}
bribeWhiteListedRewardToken[_token] = false;
for (uint256 i = 0; i < bribeWhiteListedRewardTokenList.length; i++) {
if (bribeWhiteListedRewardTokenList[i] == _token) {
bribeWhiteListedRewardTokenList[i] =
bribeWhiteListedRewardTokenList[bribeWhiteListedRewardTokenList.length - 1];
bribeWhiteListedRewardTokenList.pop();
break;
}
}
emit RemoveWhitelistedBribeToken(_token);
}
function removeRewardToken(address _token) external onlyRole(ADMIN_ROLE) {
if (!isRewardToken[_token]) {
revert VotingErrors.TokenNotInList();
}
isRewardToken[_token] = false;
for (uint256 i = 0; i < rewardTokens.length; i++) {
if (rewardTokens[i] == _token) {
rewardTokens[i] = rewardTokens[rewardTokens.length - 1];
rewardTokens.pop();
break;
}
}
emit RewardTokenRemoved(_token);
}
function setEpochRewards(address[] calldata _tokens, uint256[] calldata _rewards) external onlyRole(ADMIN_ROLE) {
if (_tokens.length != _rewards.length) {
revert VotingErrors.ArrayLengthMismatch();
}
for (uint256 i = 0; i < _tokens.length; i++) {
if (!isRewardToken[_tokens[i]]) {
revert VotingErrors.TokenNotAdded();
}
epochRewards[_tokens[i]] = _rewards[i];
}
}
function setCoolDownPeriod(uint256 _coolDownPeriod) external onlyRole(ADMIN_ROLE) {
COOL_DOWN_PERIOD = _coolDownPeriod;
emit CoolDownPeriodUpdated(_coolDownPeriod);
}
function notifyRewardAmount(address[] calldata _tokens, uint256[] calldata _amounts)
external
whenNotPaused
nonReentrant
onlyRole(ADMIN_ROLE)
{
if (_tokens.length != _amounts.length) {
revert VotingErrors.ArrayLengthMismatch();
}
for (uint256 i = 0; i < _tokens.length; i++) {
if (!isRewardToken[_tokens[i]]) {
revert VotingErrors.TokenNotAdded();
}
if (_amounts[i] <= 0) {
revert VotingErrors.RewardAmountZero();
}
IERC20(_tokens[i]).transferFrom(msg.sender, address(this), _amounts[i]);
}
emit RewardNotified(_tokens, _amounts);
}
function killPool(address poolAddress) external onlyRole(EMERGENCY_COUNCIL_ROLE) {
if (!isAlive[poolAddress]) {
revert VotingErrors.PoolAlreadyKilled();
}
isAlive[poolAddress] = false;
emit PoolKilled(poolAddress);
}
function revivePool(address poolAddress) external onlyRole(EMERGENCY_COUNCIL_ROLE) {
if (isAlive[poolAddress]) {
revert VotingErrors.PoolAlreadyActive();
}
isAlive[poolAddress] = true;
emit PoolRevived(poolAddress);
}
function registerPool(address poolAddress) external onlyRole(ADMIN_ROLE) {
if (pools[poolAddress].isRegistered) {
revert VotingErrors.PoolAlreadyRegistered();
}
address newFeeDistributor = IIncentiveManagerFactory(incentiveManagerFactory).createIncentiveManager(
address(this), address(this), IIncentiveManagerFactory.RewardType.FEE_SHARE, msg.sender
);
address newBribe = IIncentiveManagerFactory(incentiveManagerFactory).createIncentiveManager(
address(this), address(this), IIncentiveManagerFactory.RewardType.BRIBE, msg.sender
);
pools[poolAddress] =
Pool({totalVotes: 0, isRegistered: true, feeDistributor: newFeeDistributor, bribeDistributor: newBribe});
poolAddresses.push(poolAddress);
poolCount++;
isAlive[poolAddress] = true;
emit PoolRegistered(poolAddress, newFeeDistributor, newBribe);
}
function vote(uint256 nftId, address[] memory _poolAddresses, uint256[] memory weights)
public
onlyDuringEpoch(nftId)
whenNotPaused
nonReentrant
{
if (block.timestamp < startOfVoteEpoch) {
revert VotingErrors.VotingNotStarted();
}
uint256 currentEpoch = getCurrentEpoch();
// Check: Ensure the caller is approved or owns the NFT
if (!veStellaToken.isApprovedOrOwner(msg.sender, nftId)) {
revert VotingErrors.NotApprovedOrOwner();
}
// Check: Ensure arrays are of equal length
if (_poolAddresses.length != weights.length) {
revert VotingErrors.ArrayLengthMismatch();
}
// Check: Ensure maximum votes per epoch is not exceeded
if (_poolAddresses.length > MAX_VOTES_PER_EPOCH) {
revert VotingErrors.ExceedingMaxVotes();
}
// Check: Ensure NFT has voting weight
uint256 voterWeight = veStellaToken.balanceOfNFT(nftId);
if (voterWeight <= 0) {
revert VotingErrors.NoVoterWeight();
}
// Check: Validate pool state and calculate total weight
uint256 totalWeights = 0;
for (uint256 i = 0; i < _poolAddresses.length; i++) {
if (!isAlive[_poolAddresses[i]]) {
revert VotingErrors.PoolKilled();
}
if (weights[i] <= 0) {
revert VotingErrors.WeightCannotBeZero();
}
totalWeights += weights[i];
}
nftVoteData[nftId].epoch = currentEpoch;
_vote(nftId, _poolAddresses, weights, voterWeight, totalWeights);
}
function _vote(
uint256 nftId,
address[] memory _poolAddresses,
uint256[] memory weights,
uint256 voterWeight,
uint256 totalWeights
) internal {
VoteCheckpoint storage voteData = nftVoteData[nftId];
_reset(nftId);
// Cast votes for each pool
uint256 currentTotal = totalVotes;
for (uint256 i = 0; i < _poolAddresses.length; i++) {
address poolAddress = _poolAddresses[i];
uint256 weight = (weights[i] * voterWeight) / totalWeights;
// Record votes for fee and bribe distributors
IIncentiveManager(pools[poolAddress].feeDistributor).recordVote(weight, nftId);
IIncentiveManager(pools[poolAddress].bribeDistributor).recordVote(weight, nftId);
pools[poolAddress].totalVotes += weight;
voteData.poolVotes[poolAddress] += weight;
totalVotes += weight;
}
voteData.isVoting = true;
voteData.pools = _poolAddresses;
voteData.totalVotes += (totalVotes - currentTotal);
emit VoteCast(nftId, _poolAddresses, weights);
}
function distributeRewards(uint256 batchSize) external whenNotPaused onlyRole(ADMIN_ROLE) nonReentrant {
if (getCurrentEpoch() <= lastEpochTime) {
revert VotingErrors.EpochNotFinished();
}
if (!epochRewardDistributed[getCurrentEpoch() - 1]) {
IMinter(minter).mintForEpoch();
epochRewardDistributed[getCurrentEpoch() - 1] = true;
}
uint256 currentTotalVotes = totalVotes;
uint256 endIndex = processedPools + batchSize;
if (endIndex > poolAddresses.length) {
endIndex = poolAddresses.length;
}
for (uint256 i = processedPools; i < endIndex; i++) {
address poolAddress = poolAddresses[i];
uint256 poolTotalVotes = pools[poolAddress].totalVotes;
if (poolTotalVotes == 0 || !isAlive[poolAddress]) {
continue;
}
uint256 poolPercentage = (poolTotalVotes * 1e18) / currentTotalVotes;
for (uint256 j = 0; j < rewardTokens.length; j++) {
address token = rewardTokens[j];
uint256 rewardAmount = (epochRewards[token] * poolPercentage) / 1e18;
if (rewardAmount > 0) {
address rewarder = IRewardRegistry(rewardRegistryAddress).getRewarderByPool(poolAddress);
_addRewardsToOffchain(rewarder, IERC20(token), rewardAmount);
emit RewardAllocated(poolAddress, token, rewardAmount);
}
}
pools[poolAddress].totalVotes = 0;
}
processedPools = endIndex;
// If all pools have been processed, reset total votes and update epoch time
if (processedPools >= poolAddresses.length) {
totalVotes = 0;
lastEpochTime = getCurrentEpoch();
processedPools = 0; // Reset for the next epoch
emit RewardsDistributed(lastEpochTime);
}
}
function _addRewardsToOffchain(address rewarder, IERC20 token, uint256 tokenAmount) internal {
require(rewarder != address(0), "Invalid rewarder address");
uint32 startTimestamp = uint32(getCurrentEpoch()) * uint32(epochDuration);
uint32 endTimestamp = startTimestamp + uint32(epochDuration);
uint256 rewardPerSec = tokenAmount / (endTimestamp - startTimestamp);
bool isNative = address(token) == address(wrappedGLMR);
if (isNative) {
IWGLMR(wrappedGLMR).withdraw(tokenAmount);
require(address(this).balance >= tokenAmount, "Insufficient native token balance");
IRewarder(rewarder).addRewardInfo{value: tokenAmount}(
token, isNative, startTimestamp, endTimestamp, rewardPerSec
);
} else {
token.safeApprove(rewarder, tokenAmount);
require(token.allowance(address(this), rewarder) >= tokenAmount, "Approval failed");
IRewarder(rewarder).addRewardInfo(token, isNative, startTimestamp, endTimestamp, rewardPerSec);
token.safeApprove(rewarder, 0);
}
}
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 _tokenId)
external
ownerOrEscrow(_tokenId)
whenNotPaused
nonReentrant
{
uint256 _length = _bribes.length;
for (uint256 i = 0; i < _length; i++) {
IIncentiveManager(_bribes[i]).getReward(msg.sender, _tokenId, _tokens[i]);
}
}
function withdrawFromManagedNft(uint256 _tokenId) external whenNotPaused nonReentrant onlyDuringEpoch(_tokenId) {
if (!veStellaToken.isApprovedOrOwner(msg.sender, _tokenId)) revert VotingErrors.NotApprovedOrOwner();
uint256 managedTokenId = (veStellaToken.managedInfo(_tokenId)).mNFTId;
veStellaToken.withdrawFromManagedNFT(_tokenId);
uint256 weight = veStellaToken.balanceOfNFT(managedTokenId);
if (weight == 0) {
_reset(managedTokenId);
} else {
_poke(managedTokenId, weight);
}
}
function depositIntoManagedNft(uint256 _tokenId, uint256 _managedTokenId)
external
whenNotPaused
nonReentrant
onlyDuringEpoch(_tokenId)
{
if (!veStellaToken.isApprovedOrOwner(msg.sender, _tokenId)) revert VotingErrors.NotApprovedOrOwner();
if (veStellaToken.lockType(_tokenId) != IVEStella.LockType.NORMAL) revert VotingErrors.NotNormalNFT();
veStellaToken.depositIntoManagedNFT(_tokenId, _managedTokenId);
uint256 voterWeight = veStellaToken.balanceOfNFT(_managedTokenId);
nftVoteData[_tokenId].epoch = getCurrentEpoch();
_poke(_managedTokenId, voterWeight);
}
function _poke(uint256 _tokenId, uint256 _voterWeight) internal {
VoteCheckpoint storage voteData = nftVoteData[_tokenId];
// Retrieve pools and weights from the existing vote checkpoint
address[] memory NftPoolAddresses = voteData.pools;
uint256[] memory weights = new uint256[](NftPoolAddresses.length);
// Calculate total weight of the existing votes
uint256 totalWeights = 0;
for (uint256 i = 0; i < NftPoolAddresses.length; i++) {
weights[i] = voteData.poolVotes[NftPoolAddresses[i]];
totalWeights += weights[i];
}
// Use _vote for the core voting logic, passing all necessary parameters
_vote(_tokenId, NftPoolAddresses, weights, _voterWeight, totalWeights);
}
function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId)
external
ownerOrEscrow(_tokenId)
whenNotPaused
nonReentrant
{
uint256 _length = _fees.length;
for (uint256 i = 0; i < _length; i++) {
IIncentiveManager(_fees[i]).getReward(msg.sender, _tokenId, _tokens[i]);
}
}
function reset(uint256 _tokenId) external whenNotPaused onlyDuringEpoch(_tokenId) ownerOrEscrow(_tokenId) nonReentrant {
// Now call the internal function to perform the reset
_reset(_tokenId);
}
function _reset(uint256 _tokenId) internal {
VoteCheckpoint storage voteData = nftVoteData[_tokenId];
bool isFromPreviousEpoch = voteData.epoch <= lastEpochTime;
// Limit local variables and avoid repeated access of storage
address[] memory localPoolAddresses = poolAddresses;
for (uint256 i = 0; i < localPoolAddresses.length; i++) {
address poolAddress = localPoolAddresses[i];
uint256 poolVotes = voteData.poolVotes[poolAddress];
if (poolVotes > 0) {
// Cache `feeDistributor` and `bribeDistributor` to reduce stack usage
address feeDistributor = pools[poolAddress].feeDistributor;
address bribeDistributor = pools[poolAddress].bribeDistributor;
IIncentiveManager(feeDistributor)._withdraw(poolVotes, _tokenId);
IIncentiveManager(bribeDistributor)._withdraw(poolVotes, _tokenId);
// If from previous epoch, pool votes were already reset to 0 during distribution
if (!isFromPreviousEpoch) {
pools[poolAddress].totalVotes -= poolVotes;
}
voteData.poolVotes[poolAddress] = 0;
}
}
// If from previous epoch, totalVotes was already reset to 0 during distribution
if (!isFromPreviousEpoch) {
totalVotes -= voteData.totalVotes;
}
voteData.totalVotes = 0;
delete voteData.pools;
voteData.isVoting = false;
}
function setMAXVOTESPEREPOCH(uint256 _maxVotesPerEpoch) external onlyRole(ADMIN_ROLE) {
if (_maxVotesPerEpoch <= 0) {
revert VotingErrors.InvalidVotes();
}
MAX_VOTES_PER_EPOCH = _maxVotesPerEpoch;
emit MaxVotesPerEpochUpdated(_maxVotesPerEpoch);
}
function getPoolsVotedByNFTForEpoch(uint256 _nftId) external view returns (address[] memory) {
return nftVoteData[_nftId].pools;
}
function getBribeWhiteListedRewardTokenList() external view returns (address[] memory) {
return bribeWhiteListedRewardTokenList;
}
function getPoolAddresses() external view returns (address[] memory) {
return poolAddresses;
}
function getRewardTokens() external view returns (address[] memory) {
return rewardTokens;
}
function getPoolVotes(address poolAddress) external view returns (uint256) {
return pools[poolAddress].totalVotes;
}
function getIsPoolRegistered(address poolAddress) external view returns (bool) {
return pools[poolAddress].isRegistered;
}
function getVeStella() external view returns (address) {
return address(veStellaToken);
}
function getCurrentEpoch() public view returns (uint256) {
return (block.timestamp / epochDuration);
}
function voted(uint256 _tokenId) external view returns (bool) {
return nftVoteData[_tokenId].isVoting;
}
function getPoolVotesForNFT(uint256 nftId, address pool) public view returns (uint256) {
return nftVoteData[nftId].poolVotes[pool];
}
function getFeeDistributorForPool(address poolAddress) external view returns (address) {
return pools[poolAddress].feeDistributor;
}
function recoverERC20(address token, uint256 amount) external onlyRole(ADMIN_ROLE) {
IERC20(token).safeTransfer(msg.sender, amount);
}
function recoverNative(uint256 amount) external onlyRole(ADMIN_ROLE) {
(bool success, ) = msg.sender.call{value: amount}("");
}
receive() external payable {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.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 making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.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 Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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'
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 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://consensys.net/diligence/blog/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.8.0/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");
(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 functionCallWithValue(target, data, 0, "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");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, 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) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// 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
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^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 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) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.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
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
interface IIncentiveManager {
function recordVote(uint256 amount, uint256 tokenId) external;
function getReward(address recipient, uint256 tokenId, address[] memory tokens) external;
function _withdraw(uint256 amount, uint256 tokenId) external;
function calculateReward(address token, uint256 tokenId) external view returns (uint256);
function notifyRewardAmount(address token, uint256 amount) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
interface IIncentiveManagerFactory {
enum RewardType {
BRIBE,
FEE_SHARE,
LOCKED_REWARDS,
FREE_REWARDS
}
function createIncentiveManager(address _votingContract, address deployer, RewardType _rewardType, address _admin) external returns (address configDistributor);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IMinter {
function setWeekly(uint256 _weekly) external;
function mintForEpoch() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IRewarder {
function initialize(address _registry) external;
function addRewardInfo(
IERC20 token,
bool _isNative,
uint32 _startTimestamp,
uint32 _endTimestamp,
uint256 _rewardPerSec
) external payable;
function getRoundedTimestamp(uint32 timestamp) external view returns (uint32);
function getRewarderByPool(address pool) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
interface IRewardRegistry {
function getRewarderByPool(address poolAddress) external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
interface IVEStella {
enum LockType {
NORMAL,
MANAGED,
MNFT
}
struct Checkpoint {
uint256 fromBlock;
uint256 votes;
}
struct LockedBalance {
int128 amount;
uint256 end;
bool isPermanent;
}
struct ManagedNFT {
address lockedReward;
address freeReward;
}
struct ManagedInfo {
uint256 mNFTId;
uint256 weight;
}
function managedNFTRewards(uint256 managedTokenId)
external
view
returns (address lockedReward, address freeReward);
function managedInfo(uint256 tokenId) external view returns (ManagedInfo memory);
function balanceOf(address account) external view returns (uint256);
function balanceOfNFT(uint256 id) external view returns (uint256);
function balanceOfNFTAt(uint256 id, uint256 timestamp) external view returns (uint256);
function totalSupply() external view returns (uint256);
function totalSupplyAt(uint256 timestamp) external view returns (uint256);
function ownerOf(uint256 tokenId) external view returns (address);
function isApprovedOrOwner(address _spender, uint256 _tokenId) external view returns (bool);
function depositIntoManagedNFT(uint256 _tokenId, uint256 _managedTokenId) external;
function withdrawFromManagedNFT(uint256 _tokenId) external;
function lockType(uint256 _tokenId) external view returns (LockType);
function increaseAmount(uint256 _tokenId, uint256 _value) external;
function token() external view returns (address);
function distributor() external view returns (address);
function locked(uint256 _tokenId) external view returns (int128 amount, uint256 end, bool isPermanent);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
interface IVoting {
// View struct without mapping for external calls
struct VoteCheckpoint {
uint256 totalVotes;
uint256 epoch;
bool isVoting;
address[] pools;
}
function getVeStella() external view returns (address);
function bribeWhiteListedRewardToken(address _token) external view returns (bool);
function claimBribes(address[] calldata _bribes, address[][] calldata _tokens, uint256 _tokenId) external;
function claimFees(address[] calldata _fees, address[][] calldata _tokens, uint256 _tokenId) external;
function vote(uint256 nftId, address[] memory _poolAddresses, uint256[] memory weights) external;
function getPoolVotes(address poolAddress) external view returns (uint256);
function nftVoteData(uint256 _tokenId) external view returns (VoteCheckpoint memory);
function voted(uint256 _tokenId) external view returns (bool);
function totalVotes() external view returns (uint256);
function getPoolsVotedByNFTForEpoch(uint256 _nftId) external view returns (address[] memory);
function getPoolVotesForNFT(uint256 nftId, address pool) external view returns (uint256);
function reset(uint256 _tokenId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IWGLMR {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function deposit() external payable;
function withdraw(uint256 amount) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
library TimeLibrary {
uint256 internal constant WEEK = 7 days;
/// @dev Returns start of epoch based on current timestamp
function epochStart(uint256 timestamp) internal pure returns (uint256) {
unchecked {
return timestamp - (timestamp % WEEK);
}
}
/// @dev Returns start of next epoch / end of current epoch
function epochNext(uint256 timestamp) internal pure returns (uint256) {
unchecked {
return timestamp - (timestamp % WEEK) + WEEK;
}
}
/// @dev Returns start of voting window
function epochVoteStart(uint256 timestamp) internal pure returns (uint256) {
unchecked {
return timestamp - (timestamp % WEEK) + 1 hours;
}
}
/// @dev Returns end of voting window / beginning of unrestricted voting window
function epochVoteEnd(uint256 timestamp) internal pure returns (uint256) {
unchecked {
return timestamp - (timestamp % WEEK) + WEEK - 1 hours;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
library VotingErrors {
error NotApprovedOrOwner();
error InvalidPool();
error VotingNotStartedForEpoch();
error EpochNotFinished();
error PoolKilled();
error PoolAlreadyRegistered();
error PoolAlreadyKilled();
error PoolAlreadyActive();
error TokenNotAdded();
error TokenNotInList();
error TokenAlreadyAdded();
error RewardAmountZero();
error ArrayLengthMismatch();
error ExceedingMaxVotes();
error AlreadyVoted();
error NoVoterWeight();
error WeightCannotBeZero();
error InvalidVotes();
error OnlyOwnerOrEscrow();
error VotingInCoolDown();
error NoVotesInEpoch();
error NotNormalNFT();
error VotingNotStarted();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IVoting} from "../interfaces/IVoting.sol";
import {TimeLibrary} from "../libraries/Timelibrary.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IIncentiveManagerFactory} from "../interfaces/IIncentiveManagerFactory.sol";
import {IVEStella} from "../interfaces/IVEStella.sol";
abstract contract BaseRewardDistributor is ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 public constant DISTRIBUTION_PERIOD = 7 days;
address public ve;
address public votingContract;
IIncentiveManagerFactory.RewardType public rewardType;
uint256 public globalBalance;
address public authorizedCaller;
mapping(uint256 => uint256) public individualBalance;
mapping(address => mapping(uint256 => uint256)) public epochRewardTokenAmounts;
mapping(address => mapping(uint256 => uint256)) public lastClaimedTimestamp;
// NEW: Track total voting power that actually participated in each epoch
mapping(uint256 => uint256) public epochTotalVotingPower;
// NEW: Track which epoch each user's votes were cast in
mapping(uint256 => mapping(uint256 => uint256)) public userEpochVotes; // tokenId => epoch => amount
// NEW: Track when users deposited into managed NFT for better reward calculation
mapping(uint256 => uint256) public userDepositEpoch;
// NEW: Track last claimed epoch per user per token
mapping(address => mapping(uint256 => uint256)) public lastClaimedEpoch;
address[] public rewardTokens;
struct BalanceCheckpoint {
uint256 checkpointTime;
uint256 individualBalance;
}
struct GlobalCheckpoint {
uint256 checkpointTime;
uint256 globalBalance;
}
mapping(uint256 => mapping(uint256 => BalanceCheckpoint)) public balanceCheckpoints;
mapping(uint256 => uint256) public checkpointCounts;
mapping(uint256 => GlobalCheckpoint) public globalBalanceCheckpoints;
uint256 public globalCheckpointCount;
struct TokenReward {
address token;
uint256 amount;
}
event RewardNotification(address indexed notifier, address indexed token, uint256 epoch, uint256 amount);
event TokensDeposited(address indexed user, uint256 indexed tokenId, uint256 amount);
event TokensWithdrawn(address indexed user, uint256 indexed tokenId, uint256 amount);
event RewardClaimed(address indexed recipient, address indexed token, uint256 reward, uint256 nftId);
constructor(address _votingContract, address _authorizedCaller, IIncentiveManagerFactory.RewardType _rewardType) {
votingContract = _votingContract;
ve = IVoting(_votingContract).getVeStella();
authorizedCaller = _authorizedCaller;
rewardType = _rewardType;
}
function findPreviousBalanceIndex(uint256 tokenId, uint256 timestamp) public view returns (uint256) {
uint256 nCheckpoints = checkpointCounts[tokenId];
if (nCheckpoints == 0) {
return 0;
}
if (balanceCheckpoints[tokenId][nCheckpoints - 1].checkpointTime <= timestamp) {
return (nCheckpoints - 1);
}
if (balanceCheckpoints[tokenId][0].checkpointTime > timestamp) {
return 0;
}
uint256 lower = 0;
uint256 upper = nCheckpoints - 1;
while (upper > lower) {
uint256 center = upper - (upper - lower) / 2;
BalanceCheckpoint memory cp = balanceCheckpoints[tokenId][center];
if (cp.checkpointTime == timestamp) {
return center;
} else if (cp.checkpointTime < timestamp) {
lower = center;
} else {
upper = center - 1;
}
}
return lower;
}
function findPreviousGlobalIndex(uint256 timestamp) public view returns (uint256) {
uint256 nCheckpoints = globalCheckpointCount;
if (nCheckpoints == 0) {
return 0;
}
if (globalBalanceCheckpoints[nCheckpoints - 1].checkpointTime <= timestamp) {
return (nCheckpoints - 1);
}
if (globalBalanceCheckpoints[0].checkpointTime > timestamp) {
return 0;
}
uint256 lower = 0;
uint256 upper = nCheckpoints - 1;
while (upper > lower) {
uint256 center = upper - (upper - lower) / 2;
GlobalCheckpoint memory cp = globalBalanceCheckpoints[center];
if (cp.checkpointTime == timestamp) {
return center;
} else if (cp.checkpointTime < timestamp) {
lower = center;
} else {
upper = center - 1;
}
}
return lower;
}
function _logCheckpoint(uint256 tokenId, uint256 balance) internal {
uint256 nCheckpoints = checkpointCounts[tokenId];
uint256 timestamp = block.timestamp;
if (
nCheckpoints > 0
&& TimeLibrary.epochStart(balanceCheckpoints[tokenId][nCheckpoints - 1].checkpointTime)
== TimeLibrary.epochStart(timestamp)
) {
balanceCheckpoints[tokenId][nCheckpoints - 1] = BalanceCheckpoint(timestamp, balance);
} else {
balanceCheckpoints[tokenId][nCheckpoints] = BalanceCheckpoint(timestamp, balance);
checkpointCounts[tokenId] = nCheckpoints + 1;
}
}
function _logGlobalCheckpoint() internal {
uint256 nCheckpoints = globalCheckpointCount;
uint256 timestamp = block.timestamp;
if (
nCheckpoints > 0
&& TimeLibrary.epochStart(globalBalanceCheckpoints[nCheckpoints - 1].checkpointTime)
== TimeLibrary.epochStart(timestamp)
) {
globalBalanceCheckpoints[nCheckpoints - 1] = GlobalCheckpoint(timestamp, globalBalance);
} else {
globalBalanceCheckpoints[nCheckpoints] = GlobalCheckpoint(timestamp, globalBalance);
globalCheckpointCount = nCheckpoints + 1;
}
}
function recordVote(uint256 amount, uint256 tokenId) external {
require(msg.sender == authorizedCaller, "Unauthorized");
// Track epoch-specific voting power
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
epochTotalVotingPower[currentEpoch] += amount;
// NEW: Track which epoch this user's votes were cast in
userEpochVotes[tokenId][currentEpoch] += amount;
// NEW: Track when user first deposited (store epoch start time, not epoch number)
if (userDepositEpoch[tokenId] == 0) {
userDepositEpoch[tokenId] = currentEpoch;
}
globalBalance += amount;
individualBalance[tokenId] += amount;
_logCheckpoint(tokenId, individualBalance[tokenId]);
_logGlobalCheckpoint();
emit TokensDeposited(msg.sender, tokenId, amount);
}
function _withdraw(uint256 amount, uint256 tokenId) external {
require(msg.sender == authorizedCaller, "Unauthorized");
// CORRECT FINAL FIX: Never touch historical voting records
// Only modify current epoch and track global/individual balances
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
// Only clear current epoch votes (if any) - never touch historical epochs
uint256 userVotesInCurrentEpoch = userEpochVotes[tokenId][currentEpoch];
if (userVotesInCurrentEpoch > 0) {
epochTotalVotingPower[currentEpoch] -= userVotesInCurrentEpoch;
userEpochVotes[tokenId][currentEpoch] = 0;
}
// Update global tracking (for individual balance management)
globalBalance -= amount;
individualBalance[tokenId] -= amount;
_logCheckpoint(tokenId, individualBalance[tokenId]);
_logGlobalCheckpoint();
emit TokensWithdrawn(msg.sender, tokenId, amount);
}
function _notifyRewardAmount(address token, uint256 amount) internal virtual {
require(amount != 0, "Invalid amount zero");
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
epochRewardTokenAmounts[token][currentEpoch] += amount;
bool tokenExists = false;
for (uint256 i = 0; i < rewardTokens.length; i++) {
if (rewardTokens[i] == token) {
tokenExists = true;
break;
}
}
if (!tokenExists) {
rewardTokens.push(token);
}
emit RewardNotification(msg.sender, token, currentEpoch, amount);
}
function getReward(address recipient, uint256 tokenId, address[] memory tokens) external {
if (rewardType != IIncentiveManagerFactory.RewardType.FREE_REWARDS) {
require(msg.sender == authorizedCaller, "Unauthorized");
} else {
require(IVEStella(ve).isApprovedOrOwner(recipient, tokenId), "Not owner");
}
uint256 length = tokens.length;
for (uint256 i = 0; i < length; i++) {
uint256 rewardAmount = calculateReward(tokens[i], tokenId);
lastClaimedTimestamp[tokens[i]][tokenId] = block.timestamp;
// IMPROVED: Track last claimed epoch for managed NFT users with chunked claiming support
if (rewardType == IIncentiveManagerFactory.RewardType.FREE_REWARDS) {
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
uint256 userDepositEpochStart = userDepositEpoch[tokenId];
uint256 userLastClaimedEpochStart = lastClaimedEpoch[tokens[i]][tokenId];
uint256 startEpoch = userDepositEpochStart > userLastClaimedEpochStart ? userDepositEpochStart : userLastClaimedEpochStart;
if (startEpoch > 0 && startEpoch < currentEpoch) {
uint256 epochsToProcess = (currentEpoch - startEpoch) / DISTRIBUTION_PERIOD;
uint256 maxEpochs = 52;
if (epochsToProcess > maxEpochs) {
// We're doing chunked claiming - only update to the end of the chunk we actually claimed
lastClaimedEpoch[tokens[i]][tokenId] = startEpoch + (maxEpochs * DISTRIBUTION_PERIOD);
} else {
// Normal claiming - update to current epoch
lastClaimedEpoch[tokens[i]][tokenId] = currentEpoch;
}
} else {
// Fallback to current epoch
lastClaimedEpoch[tokens[i]][tokenId] = currentEpoch;
}
}
if (rewardAmount > 0) IERC20(tokens[i]).safeTransfer(recipient, rewardAmount);
emit RewardClaimed(recipient, tokens[i], rewardAmount, tokenId);
}
}
function calculateReward(address token, uint256 tokenId) public view returns (uint256) {
if (checkpointCounts[tokenId] == 0) {
return 0;
}
// NEW: Check if this is a managed NFT user
if (rewardType == IIncentiveManagerFactory.RewardType.FREE_REWARDS) {
return calculateManagedNFTReward(token, tokenId);
}
uint256 totalReward = 0;
uint256 startTime = TimeLibrary.epochStart(lastClaimedTimestamp[token][tokenId]);
uint256 index = findPreviousBalanceIndex(tokenId, startTime);
BalanceCheckpoint memory cp0 = balanceCheckpoints[tokenId][index];
startTime = Math.max(startTime, TimeLibrary.epochStart(cp0.checkpointTime));
uint256 epochCount = (TimeLibrary.epochStart(block.timestamp) - startTime) / DISTRIBUTION_PERIOD;
if (epochCount > 0) {
for (uint256 i = 0; i < epochCount; i++) {
// Check if user actually voted in this specific epoch
uint256 userBalanceInEpoch = getBalanceAtEpoch(tokenId, startTime);
if (userBalanceInEpoch > 0) {
// Use epoch-specific voting power instead of global total supply
// This ensures 100% of epoch rewards get distributed among actual voters
uint256 epochVotingPower = epochTotalVotingPower[startTime];
if (epochVotingPower > 0) {
totalReward += (userBalanceInEpoch * epochRewardTokenAmounts[token][startTime]) / epochVotingPower;
}
}
startTime += DISTRIBUTION_PERIOD;
}
}
return totalReward;
}
// NEW: Calculate rewards for managed NFT users (proportional ownership)
function calculateManagedNFTReward(address token, uint256 tokenId) public view returns (uint256) {
// Get user's current balance (their recorded voting weight)
uint256 userBalance = individualBalance[tokenId];
if (userBalance == 0) return 0;
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
// Calculate from user's deposit epoch or last claimed epoch (whichever is later)
uint256 userDepositEpochStart = userDepositEpoch[tokenId];
uint256 userLastClaimedEpochStart = lastClaimedEpoch[token][tokenId];
uint256 startEpoch = userDepositEpochStart > userLastClaimedEpochStart ? userDepositEpochStart : userLastClaimedEpochStart;
// If user hasn't deposited yet or startEpoch is invalid, return 0
if (startEpoch == 0 || startEpoch >= currentEpoch) return 0;
// Calculate total epochs available
uint256 epochsToProcess = (currentEpoch - startEpoch) / DISTRIBUTION_PERIOD;
// IMPROVED CHUNKED CLAIMING: Process first chunk if too many epochs
uint256 maxEpochs = 52;
uint256 endEpoch = currentEpoch;
if (epochsToProcess > maxEpochs) {
// ✅ BETTER: Claim first 52 epochs instead of most recent 52
// This way user can claim again later for remaining epochs
endEpoch = startEpoch + (maxEpochs * DISTRIBUTION_PERIOD);
}
// Calculate total rewards available for this chunk
uint256 totalAvailableRewards = 0;
// Sum rewards from startEpoch to endEpoch (exclusive for actual claiming)
for (uint256 epochStart = startEpoch; epochStart < endEpoch; epochStart += DISTRIBUTION_PERIOD) {
totalAvailableRewards += epochRewardTokenAmounts[token][epochStart];
}
if (totalAvailableRewards == 0 || globalBalance == 0) return 0;
// Return proportional share based on user's ownership percentage
return (userBalance * totalAvailableRewards) / globalBalance;
}
// Get balance only if user voted during this specific epoch
function getBalanceAtEpoch(uint256 tokenId, uint256 epochStart) public view returns (uint256) {
// CORRECT FINAL FIX: Return original voting records for completed epochs
// Historical voting records are preserved and never modified after epoch completion
return userEpochVotes[tokenId][epochStart];
}
function getTotalRewards(uint256 tokenId) public view returns (TokenReward[] memory) {
TokenReward[] memory rewards = new TokenReward[](rewardTokens.length);
for (uint256 i = 0; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
uint256 rewardAmount = calculateReward(token, tokenId);
rewards[i] = TokenReward({token: token, amount: rewardAmount});
}
return rewards;
}
function getRewardTokens() external view returns (address[] memory) {
return rewardTokens;
}
function getEstimatedRewards(address token, uint256 tokenId) public view returns (uint256) {
if (checkpointCounts[tokenId] == 0) {
return 0;
}
// NEW: Use managed NFT calculation for FREE_REWARDS
if (rewardType == IIncentiveManagerFactory.RewardType.FREE_REWARDS) {
return getEstimatedManagedNFTReward(token, tokenId);
}
// First calculate claimable rewards from completed epochs using the new logic
uint256 totalReward = calculateReward(token, tokenId);
// Now add the pending reward from current epoch only if user voted in current epoch
uint256 currentEpochStart = TimeLibrary.epochStart(block.timestamp);
uint256 userBalanceInCurrentEpoch = getBalanceAtEpoch(tokenId, currentEpochStart);
if (userBalanceInCurrentEpoch > 0) {
// Use epoch-specific voting power for current epoch too
uint256 currentEpochVotingPower = epochTotalVotingPower[currentEpochStart];
if (currentEpochVotingPower > 0) {
// Add the current epoch's pending rewards only if user voted this epoch
totalReward += (userBalanceInCurrentEpoch * epochRewardTokenAmounts[token][currentEpochStart]) / currentEpochVotingPower;
}
}
return totalReward;
}
// NEW: Get estimated rewards for managed NFT users (including current epoch)
function getEstimatedManagedNFTReward(address token, uint256 tokenId) public view returns (uint256) {
// Get user's current balance (their recorded voting weight)
uint256 userBalance = individualBalance[tokenId];
if (userBalance == 0) return 0;
uint256 currentEpoch = TimeLibrary.epochStart(block.timestamp);
// Calculate from user's deposit epoch or last claimed epoch (whichever is later)
uint256 userDepositEpochStart = userDepositEpoch[tokenId];
uint256 userLastClaimedEpochStart = lastClaimedEpoch[token][tokenId];
uint256 startEpoch = userDepositEpochStart > userLastClaimedEpochStart ? userDepositEpochStart : userLastClaimedEpochStart;
// If user hasn't deposited yet or startEpoch is invalid, return 0
if (startEpoch == 0 || startEpoch > currentEpoch) return 0;
// Calculate total epochs available
uint256 epochsToProcess = (currentEpoch - startEpoch) / DISTRIBUTION_PERIOD + 1; // +1 to include current epoch
// IMPROVED CHUNKED CLAIMING: Process first chunk if too many epochs
uint256 maxEpochs = 52;
uint256 endEpoch = currentEpoch;
if (epochsToProcess > maxEpochs) {
// ✅ BETTER: Claim first 52 epochs instead of most recent 52
// This way user can claim again later for remaining epochs
endEpoch = startEpoch + (maxEpochs * DISTRIBUTION_PERIOD);
// Don't include current epoch if we're chunking
if (endEpoch > currentEpoch) {
endEpoch = currentEpoch;
}
}
// Calculate total rewards available for this chunk
uint256 totalAvailableRewards = 0;
// Sum rewards from startEpoch to endEpoch
for (uint256 epochStart = startEpoch; epochStart <= endEpoch; epochStart += DISTRIBUTION_PERIOD) {
totalAvailableRewards += epochRewardTokenAmounts[token][epochStart];
}
if (totalAvailableRewards == 0 || globalBalance == 0) return 0;
// Return proportional share based on user's ownership percentage
return (userBalance * totalAvailableRewards) / globalBalance;
}
function getEstimatedTotalRewards(uint256 tokenId) public view returns (TokenReward[] memory) {
TokenReward[] memory rewards = new TokenReward[](rewardTokens.length);
for (uint256 i = 0; i < rewardTokens.length; i++) {
address token = rewardTokens[i];
uint256 rewardAmount = getEstimatedRewards(token, tokenId);
rewards[i] = TokenReward({token: token, amount: rewardAmount});
}
return rewards;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import {BaseRewardDistributor} from "./BaseRewardDistributor.sol";
import {IVoting} from "../interfaces/IVoting.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IIncentiveManagerFactory} from "../interfaces/IIncentiveManagerFactory.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract IncentiveManager is BaseRewardDistributor, AccessControl {
bytes32 public constant FEE_ADDER_ROLE = keccak256("FEE_ADDER_ROLE");
IIncentiveManagerFactory.RewardType public RewardType;
constructor(address _votingContract, address _deployer, IIncentiveManagerFactory.RewardType _rewardType, address _admin)
BaseRewardDistributor(_votingContract, _deployer, _rewardType)
{
RewardType = _rewardType;
votingContract = _votingContract;
_setupRole(DEFAULT_ADMIN_ROLE, _admin);
_setupRole(FEE_ADDER_ROLE, _admin);
_setupRole(FEE_ADDER_ROLE, _deployer);
}
function notifyRewardAmount(address token, uint256 amount) external {
if (RewardType == IIncentiveManagerFactory.RewardType.BRIBE) {
require(IVoting(votingContract).bribeWhiteListedRewardToken(token), "Bribe: Token not whitelisted");
} else if (RewardType == IIncentiveManagerFactory.RewardType.FEE_SHARE) {
require(hasRole(FEE_ADDER_ROLE, msg.sender), "Caller is not a fee adder");
} else if (RewardType == IIncentiveManagerFactory.RewardType.LOCKED_REWARDS) {
require(msg.sender == ve, "Only veStella can call");
} else if (RewardType == IIncentiveManagerFactory.RewardType.FREE_REWARDS) {
require(IVoting(votingContract).bribeWhiteListedRewardToken(token), "Free rewards: Token not whitelisted");
}
super._notifyRewardAmount(token, amount);
}
function changeAdmin(address _admin) external onlyRole(DEFAULT_ADMIN_ROLE) {
_setupRole(DEFAULT_ADMIN_ROLE, _admin);
}
function recoverERC20(address token, uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
IERC20(token).transfer(msg.sender, amount);
}
function getTotalPendingRewards(uint256 tokenId) external view returns (TokenReward[] memory) {
return getTotalRewards(tokenId);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_veStellaToken","type":"address"},{"internalType":"address","name":"_WGLMR","type":"address"},{"internalType":"address","name":"_rewardRegistryAddress","type":"address"},{"internalType":"address","name":"_incentiveManagerFactory","type":"address"},{"internalType":"address","name":"_minter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyVoted","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"EpochNotFinished","type":"error"},{"inputs":[],"name":"ExceedingMaxVotes","type":"error"},{"inputs":[],"name":"InvalidVotes","type":"error"},{"inputs":[],"name":"NoVoterWeight","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"NotNormalNFT","type":"error"},{"inputs":[],"name":"OnlyOwnerOrEscrow","type":"error"},{"inputs":[],"name":"PoolAlreadyActive","type":"error"},{"inputs":[],"name":"PoolAlreadyKilled","type":"error"},{"inputs":[],"name":"PoolAlreadyRegistered","type":"error"},{"inputs":[],"name":"PoolKilled","type":"error"},{"inputs":[],"name":"RewardAmountZero","type":"error"},{"inputs":[],"name":"TokenAlreadyAdded","type":"error"},{"inputs":[],"name":"TokenNotAdded","type":"error"},{"inputs":[],"name":"TokenNotInList","type":"error"},{"inputs":[],"name":"VotingInCoolDown","type":"error"},{"inputs":[],"name":"VotingNotStarted","type":"error"},{"inputs":[],"name":"WeightCannotBeZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"AddWhitelistedBribeToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_newCoolDownPeriod","type":"uint256"}],"name":"CoolDownPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_perEpoch","type":"uint256"}],"name":"MaxVotesPerEpochUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"}],"name":"PoolKilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"address","name":"feeDistributor","type":"address"},{"indexed":false,"internalType":"address","name":"bribe","type":"address"}],"name":"PoolRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolAddress","type":"address"}],"name":"PoolRevived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RemoveWhitelistedBribeToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolId","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAllocated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"RewardNotified","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RewardTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RewardTokenRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"RewardsDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"voterNft","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"poolIds","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"VoteCast","type":"event"},{"inputs":[],"name":"ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COOL_DOWN_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMERGENCY_COUNCIL_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_VOTES_PER_EPOCH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"addRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"addWhitelistedBribeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bribeWhiteListedRewardToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bribeWhiteListedRewardTokenList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bribes","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_fees","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_managedTokenId","type":"uint256"}],"name":"depositIntoManagedNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchSize","type":"uint256"}],"name":"distributeRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emergencyPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"epochRewardDistributed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"epochRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBribeWhiteListedRewardTokenList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"getFeeDistributorForPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"getIsPoolRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolAddresses","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"getPoolVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"address","name":"pool","type":"address"}],"name":"getPoolVotesForNFT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_nftId","type":"uint256"}],"name":"getPoolsVotedByNFTForEpoch","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVeStella","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentiveManagerFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isAlive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isRewardToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"killPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastEpochTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftVoteData","outputs":[{"internalType":"uint256","name":"totalVotes","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"bool","name":"isVoting","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pools","outputs":[{"internalType":"uint256","name":"totalVotes","type":"uint256"},{"internalType":"bool","name":"isRegistered","type":"bool"},{"internalType":"address","name":"feeDistributor","type":"address"},{"internalType":"address","name":"bribeDistributor","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"processedPools","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"registerPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"removeRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"removeWhitelistedBribeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"reset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"}],"name":"revivePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_coolDownPeriod","type":"uint256"}],"name":"setCoolDownPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_rewards","type":"uint256[]"}],"name":"setEpochRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_incentiveManagerFactory","type":"address"}],"name":"setIncentiveManagerFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxVotesPerEpoch","type":"uint256"}],"name":"setMAXVOTESPEREPOCH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_startOfVoteEpoch","type":"uint256"}],"name":"setStartOfVoteEpoch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startOfVoteEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_toggle","type":"bool"}],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veStellaToken","outputs":[{"internalType":"contract IVEStella","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nftId","type":"uint256"},{"internalType":"address[]","name":"_poolAddresses","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"voted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"withdrawFromManagedNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrappedGLMR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
6080346200025a57601f620042c838819003918201601f191683019291906001600160401b038411838510176200025f578160a092849260409687528339810103126200025a57620000518162000275565b906020916200006283830162000275565b916200007085820162000275565b6200008c6080620000846060850162000275565b930162000275565b600193600095858755601e60035562093a809485600655610e10600855636865c800600a55600e549560018060a01b039586809481938260018060a01b0319981688600254161760025542046007551685600b541617600b551683600c541617600c551690600d541617600d55169060018060a81b03191617600e55818052808352838220338352835260ff84832054161562000223575b7f141f8f32ce6198eee741f695cec728bfd32d289f1acf73621fb303581000545e808352818452848320338452845260ff858420541615620001ea575b507fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c2177592838352818152848320338452815260ff858420541615620001af575b845161401d90816200028b8239f35b83835281815284832090338452528382209060ff198254161790553391600080516020620042a8833981519152339280a438808080620001a0565b80835281845284832033845284528483208260ff1982541617905533903390600080516020620042a88339815191528580a43862000161565b81805280835283822033835283528382208160ff19825416179055333383600080516020620042a88339815191528180a462000124565b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200025a5756fe608080604052600436101561001d575b50361561001b57600080fd5b005b600090813560e01c90816301ffc9a714612be6575080630754617214612bbd57806309ae51ae14612b9a5780630c39446a14612b6d5780630d15fd7714612b4f5780631703e5f914612b105780631b0f669d14612a905780631c03e6cc146129d35780631fd58b0d1461276c578063248a9ca31461274057806327c830a91461271a57806329c536e3146126d05780632b094c7c1461268d5780632f2ff15d146125da578063310bd74b146124d0578063340a73c8146124b257806336568abe1461241f5780633d09747e146123e65780633d509c97146122b25780634cc76f09146122895780634dc6901f146121d65780634ff0876a146121b857806357d159c614612171578063590d7be51461214657806359974e381461188e578063666256aa146116875780636a55e5da1461184f57806372f6988b146117bc578063736f616b1461176e57806375b238fc146117335780637715ee751461168757806378caa8c7146116205780637ac09bf7146113265780637bb7bed1146112fb5780637c37c4201461116a578063836ed0a9146111235780638588aca9146110ea57806386fdcf7d146110cc5780638980f11f1461106757806389c614b8146110495780638b07527f146110065780638b0a615e14610fcb5780638c64035a14610f865780638fbb38ff14610f545780639162c41414610c8557806391d1485414610c39578063a059b29714610c1b578063a217fddf14610bff578063a4063dbc14610b9b578063abd9084614610907578063aede3693146108d6578063b4779d6b146108b8578063b5fd73f814610879578063b97dd9e214610852578063c4f59f9b146107bd578063c58b2ae81461078e578063cb8e410d146106fc578063d547741f146106bb578063d8abc3ad14610692578063dc4ecd9014610692578063e4b6db4c1461065e578063e98470ed146105c9578063ea076999146105a0578063f141d38914610394578063f525cb6814610376578063f91edefe1461034d5763feb21b9c0361000f573461034a57602036600319011261034a576004359060135482101561034a57602061033183612d3c565b905460405160039290921b1c6001600160a01b03168152f35b80fd5b503461034a578060031936011261034a57600c546040516001600160a01b039091168152602090f35b503461034a578060031936011261034a576020600554604051908152f35b503461034a576103a336612f28565b60ff9391936103b981600e5460a01c1615613620565b6103c1613676565b6103c9612f72565b81840361058e57855b84811061047f5750506040519280604085016040865252606084019290865b8181106104545750505082820360208401528082526001600160fb1b038111610450577f4b5b1c0bc0d5b82f534c3c3116f5cd6f06f590541f3d22b47a499857d7a61e2293602092849260051b80928583013701030190a16001815580f35b8480fd5b91939091906001906001600160a01b0361046d87612ca4565b168152602090810195019291016103f1565b6001600160a01b03908161049c6104978389896135fc565b61360c565b1688526020601581528360408a2054161561057c576104bc82868a6135fc565b351561056a57806105118a946104d6610497868c8c6135fc565b166104e285898d6135fc565b6040516323b872dd60e01b81523360048201523060248201529035604482015295869283919082906064820190565b03925af192831561055f5761052c93610531575b50506135e0565b6103d2565b8161055092903d10610558575b6105488183612da1565b81019061365e565b503880610525565b503d61053e565b6040513d8b823e3d90fd5b60405163103bc8d960e21b8152600490fd5b604051630c76021160e01b8152600490fd5b60405163512509d360e11b8152600490fd5b503461034a578060031936011261034a57600d546040516001600160a01b039091168152602090f35b503461034a57602036600319011261034a576105e3612c78565b6105eb613175565b6001600160a01b03168082526010602052604082205460ff1661064c576020817fdeac1ad2bca35d08854e411674c402abcd020312a36e3cfe8aa493700da5f35b9284526010825260408420600160ff19825416179055604051908152a180f35b60405163bf6362b560e01b8152600490fd5b503461034a578060031936011261034a5761068e61067a613c4e565b604051918291602083526020830190612c3b565b0390f35b503461034a578060031936011261034a576002546040516001600160a01b039091168152602090f35b503461034a57604036600319011261034a576106f96004356106db612c8e565b9080845260016020526106f460016040862001546132bc565b613415565b80f35b503461034a5761070b36612f28565b61071793919293612f72565b80840361058e57845b84811061072b578580f35b6001600160a01b0390816107436104978389886135fc565b1687526020916015835260ff6040892054161561057c5760166107899361076b84878a6135fc565b359261077b610497868c8b6135fc565b168a525260408820556135e0565b610720565b503461034a57602036600319011261034a5760ff60406020926004358152601284522054166040519015158152f35b503461034a578060031936011261034a576040518060145480825282602080930160146000527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec9260005b858282106108335750505061081f92500383612da1565b61068e604051928284938452830190612c3b565b85546001600160a01b0316845260019586019588955093019201610808565b503461034a578060031936011261034a576020610871600654426136ff565b604051908152f35b503461034a57602036600319011261034a5760209060ff906040906001600160a01b036108a4612c78565b168152601584522054166040519015158152f35b503461034a578060031936011261034a576020600954604051908152f35b503461034a57602036600319011261034a576108f0612f72565b80808080600435335af150610903613ace565b5080f35b503461034a57602080600319360112610b9757610922612c78565b61092a612f72565b6001600160a01b03818116808552600f845260408520600190810154909491929060ff16610b855780600d54169186604051938285608481856369c8f65760e11b958683523060048401523060248401528d60448401523360648401525af1948515610b7a578295610b59575b508290608485600d54169160405194859384928352306004840152306024840152600060448401523360648401525af1908115610b4e578891610b21575b5060405192608084018481106001600160401b03821117610b0b5760405288845260028385019489865282604082019716958688528360608301951697888652898d52600f875260408d20925183558b8301915115159060ff835491610100600160a81b03905160081b169216906affffffffffffffffffffff60a81b1617179055019151166bffffffffffffffffffffffff60a01b82541617905560135495600160401b871015610b0b57610ad6606096610ab789847f61afef2e121f71511fab2583c5ac49a5dc1d7a8ad8ad91c44c8465f073a73ac49b01601355612d3c565b90919082549060031b9160018060a01b03809116831b921b1916179055565b610ae16005546135e0565b60055584885260108252604088209060ff198254161790556040519384528301526040820152a180f35b634e487b7160e01b600052604160045260246000fd5b610b419150823d8411610b47575b610b398183612da1565b8101906136cc565b386109d5565b503d610b2f565b6040513d8a823e3d90fd5b83919550610b7390823d8411610b4757610b398183612da1565b9490610997565b6040513d84823e3d90fd5b6040516359f4180f60e11b8152600490fd5b5080fd5b503461034a57602036600319011261034a576080906001600160a01b039060409082610bc5612c78565b168152600f602052208054918060026001840154930154169160405193845260ff81161515602085015260081c1660408301526060820152f35b503461034a578060031936011261034a57602090604051908152f35b503461034a578060031936011261034a576020600854604051908152f35b503461034a57604036600319011261034a576040610c55612c8e565b9160043581526001602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b503461034a57604036600319011261034a5760043560243590610cb060ff600e5460a01c1615613620565b610cb8613676565b610cc4600654426136ff565b918184526020926011845260026040862001541015610f425762093a8080420642039060085490610cf582846134b4565b421192831593610f2c575b505050610f1a5760025460405163430c208160e01b8152336004820152602481018490526001600160a01b0393918416908581604481855afa908115610ee0578791610efd575b5015610eeb576040516368bcc3fd60e01b8152600481018390528581602481855afa908115610ee0578791610ea6575b506003811015610e9257610e80578086913b15610b975781809160446040518095819362d1e98760e21b83528860048401528960248401525af1918215610e73578692610e5c575b505060249360025416604051948580926339f890b560e21b82528660048301525afa928315610e51578593610e1a575b506011610e1394610e02600654426136ff565b928752526002604086200155613d1e565b6001815580f35b9380935084813d8311610e4a575b610e328183612da1565b81010312610e4557925191926011610def565b600080fd5b503d610e28565b6040513d87823e3d90fd5b610e67919250612d73565b61045057838538610dbf565b50604051903d90823e3d90fd5b6040516317a66f3760e01b8152600490fd5b634e487b7160e01b87526021600452602487fd5b90508581813d8311610ed9575b610ebd8183612da1565b81010312610ed557516003811015610ed55738610d77565b8680fd5b503d610eb3565b6040513d89823e3d90fd5b60405163390cdd9b60e21b8152600490fd5b610f149150863d8811610558576105488183612da1565b38610d47565b604051638bfc8f4360e01b8152600490fd5b610f379350016135ef565b421015388080610d00565b604051637c9a1cf960e01b8152600490fd5b503461034a57602036600319011261034a5760ff60036040602093600435815260118552200154166040519015158152f35b503461034a57602036600319011261034a57610fa0612c78565b610fa8612f72565b60018060a01b03166bffffffffffffffffffffffff60a01b600d541617600d5580f35b503461034a578060031936011261034a5760206040517f141f8f32ce6198eee741f695cec728bfd32d289f1acf73621fb303581000545e8152f35b503461034a57602036600319011261034a576020906001600160a01b039060019060409083611033612c78565b168152600f855220015460081c16604051908152f35b503461034a578060031936011261034a576020600754604051908152f35b503461034a57604036600319011261034a576106f9611084612c78565b61108c612f72565b60405163a9059cbb60e01b602082015233602480830191909152356044808301919091528152906110be606483612da1565b6001600160a01b03166139d7565b503461034a578060031936011261034a576020600354604051908152f35b503461034a57602036600319011261034a576020906040906001600160a01b03611112612c78565b168152600f83522054604051908152f35b503461034a57602036600319011261034a576040606091600435815260116020522080549060ff600360028301549201541690604051928352602083015215156040820152f35b503461034a57602036600319011261034a57611184612c78565b61118c612f72565b6001600160a01b039081168083526017602052604083205490919060ff16156112e957818352601760205260408320805460ff191690558291905b601892835493848210156112bb5782846111e084612d05565b929054600393841b1c16146112045750506111fd919293506135e0565b91906111c7565b60001995919493918087019081116112a75791610ab7916112286112349594612d05565b9054911b1c1691612d05565b8154918215611291577f92a36d261d412d062ad8096f1b691465373c5cd6132c0c62a7db23304dc126fa93602093019061128561127083612d05565b81549060018060a01b039060031b1b19169055565b555b604051908152a180f35b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b88526011600452602488fd5b5050507f92a36d261d412d062ad8096f1b691465373c5cd6132c0c62a7db23304dc126fa9150602090611287565b604051635a1303a760e01b8152600490fd5b503461034a57602036600319011261034a576004359060145482101561034a57602061033183612cb8565b503461034a57606036600319011261034a57600435906001600160401b0360243581811161161c5761135c903690600401612dd9565b9160443591821161034a573660238301121561034a5781600401359061138182612dc2565b9261138f6040519485612da1565b82845260209260248486019160051b83010191368311611618576024859101915b83831061160857505050506113c7600654426136ff565b8582526011835260026040832001541015610f425762093a80804206420390600854906113f482846134b4565b4211928315936115f2575b505050610f1a5760ff9161141a83600e5460a01c1615613620565b611422613676565b600a5442106115e057611437600654426136ff565b60025460405163430c208160e01b81523360048201526024810189905291966001600160a01b039290918316908481604481855afa9081156115d55786916115b8575b5015610eeb57815187510361058e578151600354106115a65783602491604051928380926339f890b560e21b82528d60048301525afa908115610e51578591611579575b50801561156757849885995b83518b101561154a57846114de8c866136eb565b51168752601086528760408820541615611538576114fc8b8a6136eb565b51156115265761151a611520916115138d8c6136eb565b51906134b4565b9a6135e0565b996114ca565b604051630c8685af60e01b8152600490fd5b60405163773836eb60e11b8152600490fd5b86928985610e13948d60118b838a5252600260408920015561371f565b60405163c5ba53b960e01b8152600490fd5b90508381813d831161159f575b6115908183612da1565b81010312610e455751386114be565b503d611586565b60405163cb9c2d7160e01b8152600490fd5b6115cf9150853d8711610558576105488183612da1565b3861147a565b6040513d88823e3d90fd5b604051633fd0090160e11b8152600490fd5b6115fd9350016135ef565b4210153880806113ff565b82358152918101918591016113b0565b8380fd5b8280fd5b503461034a57602036600319011261034a5760043561163d612f72565b8015611675576020817ffa4461b64d95d803511ec24ccf3d00fbf21a1f6c5000102a98e42b69b673b3ac92600355604051908152a180f35b6040516307707caf60e51b8152600490fd5b503461034a5761169636612e3f565b60025460405163430c208160e01b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa908115610e51578591611715575b501561170357610e13926116f660ff600e5460a01c1615613620565b6116fe613676565b613b9e565b604051637174172560e11b8152600490fd5b61172d915060203d8111610558576105488183612da1565b386116da565b503461034a578060031936011261034a5760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b503461034a57602036600319011261034a577f28648db86b34efec68e71f9e1db4cb169caaad4c8132d6f4b9d4959042866b7560206004356117ae612f72565b80600855604051908152a180f35b503461034a57602036600319011261034a576117d6612c78565b6117de613175565b6001600160a01b03168082526010602052604082205460ff161561183d576020817fa21fc9f4c6fc68a99e154673b6692c3a1d92038cdbec927fe8842b12024953b9928452601082526040842060ff198154169055604051908152a180f35b6040516304b8da9960e51b8152600490fd5b503461034a57602036600319011261034a5760209060ff906040906001600160a01b0361187a612c78565b168152601784522054166040519015158152f35b503461034a57602036600319011261034a576118b260ff600e5460a01c1615613620565b6118ba612f72565b6118c2613676565b6006546118cf81426136ff565b6007541015612134576118e290426136ff565b60001990808201908111612120578252601260205260ff6040832054161561208c575b5060045460095490611919600435836134b4565b91601354808411612084575b50905b82821061198c575050806009556013541115611945576001815580f35b806004557f6d1c76d614228b523baa4dcd9539e2c713b54ff4ab3ff2d1627e7f6cd32be4426020611978600654426136ff565b8060075583600955604051908152a1610e13565b90919261199883612d3c565b9490549260018060a01b03848760031b1c168352600f60205260408320548015808015612070575b61206057670de0b6b3a76400008202918204670de0b6b3a764000014171561204c57816119ec916136ff565b91835b60145481101561201857611a0281612cb8565b60018060a01b0391549060031b1c168086526016602052670de0b6b3a7640000611a3086604089205461348b565b0480611a47575b5050611a42906135e0565b6119ef565b600c5460405163e170aec760e01b81526001600160a01b0360038d901b8b901c81166004830152909160209183916024918391165afa908115610b4e578891611ff9575b506001600160a01b03811615611fb4576006549063ffffffff80611aaf84426136ff565b169216809202918263ffffffff811603611fa05763ffffffff8316019063ffffffff8211611fa05763ffffffff8084168184160311611fa057611afe63ffffffff8085168185160316856136ff565b600b80546001600160a01b0316871493908415611cba57546001600160a01b0316803b15611cb6578c8091602460405180948193632e1a7d4d60e01b83528c60048401525af18015611cab57908d91611c97575b5050854710611c48576001600160a01b0383163b15611c4457858c949360a49363ffffffff8a94816040519a8b998a98630c26e85960e01b8a5260048a01526024890152166044870152166064850152608484015260018060a01b03165af18015610b4e578b92918991611c2b575b5050917f97e2814ff5d2cfd886169f6d9c27c303e27685bde750d0569fd838710da9941f91611c21611a4295945b604080516001600160a01b0360039590951b8e901c851681529490931660208501529183019190915281906060820190565b0390a19038611a37565b611c3791929350612d73565b610ed55789908738611bc1565b8b80fd5b60405162461bcd60e51b815260206004820152602160248201527f496e73756666696369656e74206e617469766520746f6b656e2062616c616e636044820152606560f81b6064820152608490fd5b611ca090612d73565b611c44578b38611b52565b6040513d8f823e3d90fd5b8c80fd5b509091939250841585611f20575b15611ebc5760405163095ea7b360e01b60208201526001600160a01b038516602482015260448082018790528152611d0b90611d05606482612da1565b876139d7565b604051636eb1769f60e11b81523060048201526001600160a01b03851660248201526020816044818a5afa908115611eb1579086918d91611e7c575b5010611e45576001600160a01b0384163b15611e4157604051630c26e85960e01b8152600481018790526000602482015263ffffffff9384166044820152921660648301526084820152888160a481836001600160a01b0387165af1801561055f578c9392918a91611e22575b505091611c21611a42959492611e1d7f97e2814ff5d2cfd886169f6d9c27c303e27685bde750d0569fd838710da9941f956040519063095ea7b360e01b602083015260018060a01b031660248201526000604482015260448152611e1781612d86565b856139d7565b611bef565b611e2f9192939450612d73565b611e3d57908a918838611db4565b8780fd5b8a80fd5b60405162461bcd60e51b815260206004820152600f60248201526e105c1c1c9bdd985b0819985a5b1959608a1b6044820152606490fd5b9150506020813d602011611ea9575b81611e9860209383612da1565b81010312610e455785905138611d47565b3d9150611e8b565b6040513d8e823e3d90fd5b60405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b50604051636eb1769f60e11b81523060048201526001600160a01b03851660248201526020816044818a5afa908115611f9457600091611f62575b5015611cc8565b906020823d602011611f8c575b81611f7c60209383612da1565b8101031261034a57505138611f5b565b3d9150611f6f565b6040513d6000823e3d90fd5b634e487b7160e01b8a52601160045260248afd5b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265776172646572206164647265737300000000000000006044820152606490fd5b612012915060203d602011610b4757610b398183612da1565b38611a8b565b5060039690961b9390931c6001600160a01b0316825250600f602052604081208190559291612046906135e0565b90611928565b634e487b7160e01b84526011600452602484fd5b50509250926120469194506135e0565b50601060205260ff604086205416156119c0565b925038611925565b600e5482906001600160a01b0316803b15610b97578180916004604051809481936301f564d960e71b83525af18015610b7a5761210c575b50506120d2600654426136ff565b9081019081116120f8578152601260205260408120805460ff1916600117905538611905565b634e487b7160e01b82526011600452602482fd5b61211590612d73565b610b975781386120c4565b634e487b7160e01b83526011600452602483fd5b60405163ec76655760e01b8152600490fd5b503461034a57602036600319011261034a576004359060185482101561034a57602061033183612d05565b503461034a57602036600319011261034a57600435801515809103610b9757612198612f72565b600e805460ff60a01b191660a09290921b60ff60a01b1691909117905580f35b503461034a578060031936011261034a576020600654604051908152f35b503461034a57602036600319011261034a576121f0612c78565b6121f8612f72565b6001600160a01b0381168083526017602052604083205490919060ff1661227757818352601760205260408320805460ff1916600117905560185491600160401b831015610b0b57611287602092610ab78560017f1761ea52e8e8eaef6dd99a3c7fe7b2494372d7a8fd6d5560ea0dce99b2e706769701601855612d05565b60405163630b374b60e01b8152600490fd5b503461034a578060031936011261034a57600b546040516001600160a01b039091168152602090f35b503461034a57602036600319011261034a576122cc612c78565b6122d4612f72565b6001600160a01b039081168083526015602052604083205490919060ff16156112e957818352601560205260408320805460ff191690558291905b601492835493848210156123b857828461232884612cb8565b929054600393841b1c161461234c575050612345919293506135e0565b919061230f565b60001995919493918087019081116112a75791610ab79161237061237c9594612cb8565b9054911b1c1691612cb8565b8154918215611291577f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf500993602093019061128561127083612cb8565b5050507f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf50099150602090611287565b503461034a57602036600319011261034a576020906040906001600160a01b0361240e612c78565b168152601683522054604051908152f35b503461034a57604036600319011261034a57612439612c8e565b336001600160a01b03821603612455576106f990600435613415565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b503461034a578060031936011261034a576020600a54604051908152f35b503461034a57602036600319011261034a576004356124f760ff600e5460a01c1615613620565b612503600654426136ff565b818352601160205260026040842001541015610f425762093a808042064203906008549061253182846134b4565b4211928315936125c4575b505050610f1a5760025460405163430c208160e01b81523360048201526024810183905290602090829060449082906001600160a01b03165afa9081156125b957839161259b575b501561170357610e1390612596613676565b613ded565b6125b3915060203d8111610558576105488183612da1565b38612584565b6040513d85823e3d90fd5b6125cf9350016135ef565b42101538808061253c565b503461034a57604036600319011261034a576004356125f7612c8e565b818352600160205261260f60016040852001546132bc565b8183526001602052604083209060018060a01b0316908160005260205260ff604060002054161561263e578280f35b818352600160205260408320816000526020526040600020600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8480a438808280f35b503461034a57602036600319011261034a5760209060ff906001906040906001600160a01b036126bb612c78565b168152600f8552200154166040519015158152f35b503461034a57604036600319011261034a57600160406126ee612c8e565b926004358152601160205220019060018060a01b03166000526020526020604060002054604051908152f35b503461034a578060031936011261034a57602060ff600e5460a01c166040519015158152f35b503461034a57602036600319011261034a57600160406020926004358152828452200154604051908152f35b503461034a57602080600319360112610b97576004359061279560ff600e5460a01c1615613620565b61279d613676565b6127a9600654426136ff565b8284526011825260026040852001541015610f425762093a80804206420390600854906127d682846134b4565b4211928315936129bd575b505050610f1a5760025460405163430c208160e01b8152336004820152602481018490526001600160a01b0391821693908381604481885afa9081156115d55786916129a0575b5015610eeb57849060405163055c87a560e31b8152816004820152604081602481895afa9081156125b957839161292a575b505194803b1561161c5760248392604051948593849263c07b5b0b60e01b845260048401525af18015610e5157612914575b508160249160025416604051928380926339f890b560e21b82528760048301525afa9182156129095784926128db575b5050806128cd5750610e1390613ded565b6128d691613d1e565b610e13565b90809250813d8311612902575b6128f28183612da1565b81010312610e45575138806128bc565b503d6128e8565b6040513d86823e3d90fd5b602491946129228492612d73565b94915061288c565b919250506040813d604011612998575b8161294760409383612da1565b810103126129945760405190604082018281106001600160401b038211176129805760405280518252840151848201528591903861285a565b634e487b7160e01b88526041600452602488fd5b8580fd5b3d915061293a565b6129b79150843d8611610558576105488183612da1565b38612828565b6129c89350016135ef565b4210153880806127e1565b503461034a57602036600319011261034a576129ed612c78565b6129f5612f72565b6001600160a01b0381168083526015602052604083205490919060ff1661227757601454600160401b811015612a7c5791612a5d602092610ab78560017ff3e4c2c64e71e6ba2eaab9a599bced62f9eb91d2cda610bf41aa8c80ff2cf8269701601455612cb8565b808452601582526040808520805460ff1916600117905551908152a180f35b634e487b7160e01b84526041600452602484fd5b503461034a578060031936011261034a57604051809182601854808452602080940190601884527fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e935b85828210612af15750505061081f92500383612da1565b85546001600160a01b0316845260019586019588955093019201612ada565b503461034a57602036600319011261034a5760209060ff906040906001600160a01b03612b3b612c78565b168152601084522054166040519015158152f35b503461034a578060031936011261034a576020600454604051908152f35b503461034a57602036600319011261034a5761067a6004604061068e938235815260116020522001613cc6565b503461034a57602036600319011261034a57612bb4612f72565b600435600a5580f35b503461034a578060031936011261034a57600e546040516001600160a01b039091168152602090f35b905034610b97576020366003190112610b975760043563ffffffff60e01b811680910361161c5760209250637965db0b60e01b8114908115612c2a575b5015158152f35b6301ffc9a760e01b14905038612c23565b90815180825260208080930193019160005b828110612c5b575050505090565b83516001600160a01b031685529381019392810192600101612c4d565b600435906001600160a01b0382168203610e4557565b602435906001600160a01b0382168203610e4557565b35906001600160a01b0382168203610e4557565b601454811015612cef5760146000527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec0190600090565b634e487b7160e01b600052603260045260246000fd5b601854811015612cef5760186000527fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e0190600090565b601354811015612cef5760136000527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0900190600090565b6001600160401b038111610b0b57604052565b608081019081106001600160401b03821117610b0b57604052565b90601f801991011681019081106001600160401b03821117610b0b57604052565b6001600160401b038111610b0b5760051b60200190565b81601f82011215610e4557803591612df083612dc2565b92612dfe6040519485612da1565b808452602092838086019260051b820101928311610e45578301905b828210612e28575050505090565b838091612e3484612ca4565b815201910190612e1a565b906060600319830112610e45576001600160401b03600435818111610e455783612e6b91600401612dd9565b926024803592808411610e455782602385011215610e45578360040135612e9181612dc2565b94612e9f6040519687612da1565b818652602094848688019360051b83010194818611610e4557808301935b868510612ed35750505050505050509060443590565b8435868111610e45578891612eed85858594890101612dd9565b815201940193612ebd565b9181601f84011215610e45578235916001600160401b038311610e45576020808501948460051b010111610e4557565b6040600319820112610e45576001600160401b0391600435838111610e455782612f5491600401612ef8565b93909392602435918211610e4557612f6e91600401612ef8565b9091565b3360009081527f50efbde2d46c37e9785f1791697f77e94bb7b701e19f1930a668820722d3769460209081526040808320549092906001907fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217759060ff1615612fdb575050505050565b612fe4336134d2565b90855192612ff184612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613107575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b8401917001034b99036b4b9b9b4b733903937b6329607d1b6037840152518093868401906133c6565b01036028810185520183612da1565b5162461bcd60e51b8152918291600483016133e9565b0390fd5b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a61313785886134c1565b5360041c9280156120f85760001901919061301e565b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b81526032600452602490fd5b3360009081527fcacae11c3e803b9c57e53bab46d0843c5e8225f3d7f2d3156db434e191c2d6f660209081526040808320549092906001907f141f8f32ce6198eee741f695cec728bfd32d289f1acf73621fb303581000545e9060ff16156131de575050505050565b6131e7336134d2565b908551926131f484612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613276575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6132a685886134c1565b5360041c9280156120f857600019019190613221565b6000818152600190602091808352604093848320338452845260ff8584205416156132e8575050505050565b6132f1336134d2565b908551926132fe84612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613380575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6133b085886134c1565b5360041c9280156120f85760001901919061332b565b60005b8381106133d95750506000910152565b81810151838201526020016133c9565b6040916020825261340981518092816020860152602086860191016133c6565b601f01601f1916010190565b906000918083526001602052604083209160018060a01b03169182845260205260ff60408420541661344657505050565b8083526001602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b8181029291811591840414171561349e57565b634e487b7160e01b600052601160045260246000fd5b9190820180921161349e57565b908151811015612cef570160200190565b60405190606082018281106001600160401b03821117610b0b57604052602a8252602082016040368237825115612cef57603090538151600190811015612cef57607860218401536029905b80821161357257505061352e5790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f811660108110156135cb576f181899199a1a9b1b9c1cb0b131b232b360811b901a6135a184866134c1565b5360041c9180156135b657600019019061351e565b60246000634e487b7160e01b81526011600452fd5b60246000634e487b7160e01b81526032600452fd5b600019811461349e5760010190565b9190820391821161349e57565b9190811015612cef5760051b0190565b356001600160a01b0381168103610e455790565b1561362757565b60405162461bcd60e51b815260206004820152600f60248201526e10dbdb9d1c9858dd081c185d5cd959608a1b6044820152606490fd5b90816020910312610e4557518015158103610e455790565b600260005414613687576002600055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b90816020910312610e4557516001600160a01b0381168103610e455790565b8051821015612cef5760209160051b010190565b8115613709570490565b634e487b7160e01b600052601260045260246000fd5b92949390919483600052601160205260406000209261373d85613ded565b6004549560005b82518110156138a25788906137808661377b87613775856001600160a01b0361376d828c6136eb565b5116976136eb565b5161348b565b6136ff565b6000838152600f602052604090206001015490929060081c6001600160a01b0316803b15610e45576000809160448c60405194859384926332d91c4d60e11b84528a600485015260248401525af18015611f9457613893575b506000818152600f60205260409020600201546001600160a01b031692833b15610e45576000809460448c60405197889384926332d91c4d60e11b845287600485015260248401525af1918215611f945761387f9461387793613884575b5080600052600f60205260406000206138518382546134b4565b905560005260018901602052604060002061386d8282546134b4565b90556004546134b4565b6004556135e0565b613744565b61388d90612d73565b38613837565b61389c90612d73565b386137d9565b50969392509394905060038201600160ff19825416179055600482018451906001600160401b038211610b0b57600160401b8211610b0b5780548282558083106139ae575b506020860190600052602060002060005b8381106139915750505050613912613919916004546135ef565b82546134b4565b905561393060405192604084526040840190612c3b565b82810360208401526020808351928381520192019060005b81811061397b5750505090807fcd2605b2299581c1cf98aa371c3733db302fa37a6cfdd86dbf7dadf61aae551e920390a2565b8251845260209384019390920191600101613948565b82516001600160a01b0316818301556020909201916001016138f8565b8160005282602060002091820191015b8181106139cb57506138e7565b600081556001016139be565b60408051908101916001600160a01b03166001600160401b03831182841017610b0b57613a46926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1613a40613ace565b91613b0d565b805190828215928315613ab6575b50505015613a5f5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b613ac6935082018101910161365e565b388281613a54565b3d15613b08573d906001600160401b038211610b0b5760405191613afc601f8201601f191660200184612da1565b82523d6000602084013e565b606090565b91929015613b6f5750815115613b21575090565b3b15613b2a5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015613b825750805190602001fd5b60405162461bcd60e51b81529081906130c190600483016133e9565b9081519160005b838110613bb3575050505050565b6001600160a01b03613bc582846136eb565b5116613bd182856136eb565b5190803b15610e45576000613c13926040928983855180978195829463a44d113f60e01b84523360048501526024840152606060448401526064830190612c3b565b03925af1908115613c44575090613c309291613c35575b506135e0565b613ba5565b613c3e90612d73565b38613c2a565b513d6000823e3d90fd5b60405190601354808352826020918282019060136000527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090936000905b828210613ca357505050613ca192500383612da1565b565b85546001600160a01b031684526001958601958895509381019390910190613c8b565b9060405191828154918282526020928383019160005283600020936000905b828210613cfb57505050613ca192500383612da1565b85546001600160a01b031684526001958601958895509381019390910190613ce5565b91906000908382526020936011855260409283812091613d4060048401613cc6565b91825193613d4d85612dc2565b94613d5a88519687612da1565b808652613d69601f1991612dc2565b01368a870137819682600180809401905b613d8f575b5050505050613ca195965061371f565b909192939886518a1015613de757613ddd90613dd790838e6001600160a01b03613db98f8d6136eb565b511687525285852054613dcc8d8c6136eb565b526115138c8b6136eb565b996135e0565b9392919083613d7a565b98613d7f565b60009080825260206011815260409283812093600293848601546007541091613e14613c4e565b91849060019788948593848c01945b613e8f575b50505050505050613e7a575b808455600484019182549282815583613e5a575b505050505060030160ff198154169055565b82528120918201915b828110613e705780613e48565b8181558301613e63565b613e8784546004546135ef565b600455613e34565b8151811015613fe2576001600160a01b0380613eab83856136eb565b511690818b52868c528b888c2054918c83613ed5575b5050505050613ecf906135e0565b86613e23565b96818b829a9b9c9d949596979899600f809752209384015460081c1692015416813b15611cb6578c908b51908a63278afc8b60e21b9283815284816004978a89830152602494858301528160449889925af18015613fd457613fc5575b50813b15613fc157918f8094928f8997958f9151988997889687528601528401525af18015613fb757918b8f9b9a999897969492613ecf9694613fa8575b50613f8c575b50508a52858b52898781205590388b818c613ec1565b828d528d52613f9f898d209182546135ef565b90553880613f76565b613fb190612d73565b38613f70565b8a513d8e823e3d90fd5b8f80fd5b613fce90612d73565b38613f32565b50508f8e51903d90823e3d90fd5b613e2856fea2646970667358221220036801c05cce1be840deec2f639c2c06746c2883d6547e37cfe6c8aeacd69fe464736f6c634300081300332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d000000000000000000000000fa62b5962a7923a2910f945268aa65c943d131e9000000000000000000000000acc15dc74880c9944775448304b263d191c6077f00000000000000000000000005094f278f6da7c46aa0500c00b208e8c4899909000000000000000000000000704ba24f82ffa73b605b01e9f536d320f2d635590000000000000000000000006073675fa8e365c8f4fc402c930b50d8405a71ff
Deployed Bytecode
0x608080604052600436101561001d575b50361561001b57600080fd5b005b600090813560e01c90816301ffc9a714612be6575080630754617214612bbd57806309ae51ae14612b9a5780630c39446a14612b6d5780630d15fd7714612b4f5780631703e5f914612b105780631b0f669d14612a905780631c03e6cc146129d35780631fd58b0d1461276c578063248a9ca31461274057806327c830a91461271a57806329c536e3146126d05780632b094c7c1461268d5780632f2ff15d146125da578063310bd74b146124d0578063340a73c8146124b257806336568abe1461241f5780633d09747e146123e65780633d509c97146122b25780634cc76f09146122895780634dc6901f146121d65780634ff0876a146121b857806357d159c614612171578063590d7be51461214657806359974e381461188e578063666256aa146116875780636a55e5da1461184f57806372f6988b146117bc578063736f616b1461176e57806375b238fc146117335780637715ee751461168757806378caa8c7146116205780637ac09bf7146113265780637bb7bed1146112fb5780637c37c4201461116a578063836ed0a9146111235780638588aca9146110ea57806386fdcf7d146110cc5780638980f11f1461106757806389c614b8146110495780638b07527f146110065780638b0a615e14610fcb5780638c64035a14610f865780638fbb38ff14610f545780639162c41414610c8557806391d1485414610c39578063a059b29714610c1b578063a217fddf14610bff578063a4063dbc14610b9b578063abd9084614610907578063aede3693146108d6578063b4779d6b146108b8578063b5fd73f814610879578063b97dd9e214610852578063c4f59f9b146107bd578063c58b2ae81461078e578063cb8e410d146106fc578063d547741f146106bb578063d8abc3ad14610692578063dc4ecd9014610692578063e4b6db4c1461065e578063e98470ed146105c9578063ea076999146105a0578063f141d38914610394578063f525cb6814610376578063f91edefe1461034d5763feb21b9c0361000f573461034a57602036600319011261034a576004359060135482101561034a57602061033183612d3c565b905460405160039290921b1c6001600160a01b03168152f35b80fd5b503461034a578060031936011261034a57600c546040516001600160a01b039091168152602090f35b503461034a578060031936011261034a576020600554604051908152f35b503461034a576103a336612f28565b60ff9391936103b981600e5460a01c1615613620565b6103c1613676565b6103c9612f72565b81840361058e57855b84811061047f5750506040519280604085016040865252606084019290865b8181106104545750505082820360208401528082526001600160fb1b038111610450577f4b5b1c0bc0d5b82f534c3c3116f5cd6f06f590541f3d22b47a499857d7a61e2293602092849260051b80928583013701030190a16001815580f35b8480fd5b91939091906001906001600160a01b0361046d87612ca4565b168152602090810195019291016103f1565b6001600160a01b03908161049c6104978389896135fc565b61360c565b1688526020601581528360408a2054161561057c576104bc82868a6135fc565b351561056a57806105118a946104d6610497868c8c6135fc565b166104e285898d6135fc565b6040516323b872dd60e01b81523360048201523060248201529035604482015295869283919082906064820190565b03925af192831561055f5761052c93610531575b50506135e0565b6103d2565b8161055092903d10610558575b6105488183612da1565b81019061365e565b503880610525565b503d61053e565b6040513d8b823e3d90fd5b60405163103bc8d960e21b8152600490fd5b604051630c76021160e01b8152600490fd5b60405163512509d360e11b8152600490fd5b503461034a578060031936011261034a57600d546040516001600160a01b039091168152602090f35b503461034a57602036600319011261034a576105e3612c78565b6105eb613175565b6001600160a01b03168082526010602052604082205460ff1661064c576020817fdeac1ad2bca35d08854e411674c402abcd020312a36e3cfe8aa493700da5f35b9284526010825260408420600160ff19825416179055604051908152a180f35b60405163bf6362b560e01b8152600490fd5b503461034a578060031936011261034a5761068e61067a613c4e565b604051918291602083526020830190612c3b565b0390f35b503461034a578060031936011261034a576002546040516001600160a01b039091168152602090f35b503461034a57604036600319011261034a576106f96004356106db612c8e565b9080845260016020526106f460016040862001546132bc565b613415565b80f35b503461034a5761070b36612f28565b61071793919293612f72565b80840361058e57845b84811061072b578580f35b6001600160a01b0390816107436104978389886135fc565b1687526020916015835260ff6040892054161561057c5760166107899361076b84878a6135fc565b359261077b610497868c8b6135fc565b168a525260408820556135e0565b610720565b503461034a57602036600319011261034a5760ff60406020926004358152601284522054166040519015158152f35b503461034a578060031936011261034a576040518060145480825282602080930160146000527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec9260005b858282106108335750505061081f92500383612da1565b61068e604051928284938452830190612c3b565b85546001600160a01b0316845260019586019588955093019201610808565b503461034a578060031936011261034a576020610871600654426136ff565b604051908152f35b503461034a57602036600319011261034a5760209060ff906040906001600160a01b036108a4612c78565b168152601584522054166040519015158152f35b503461034a578060031936011261034a576020600954604051908152f35b503461034a57602036600319011261034a576108f0612f72565b80808080600435335af150610903613ace565b5080f35b503461034a57602080600319360112610b9757610922612c78565b61092a612f72565b6001600160a01b03818116808552600f845260408520600190810154909491929060ff16610b855780600d54169186604051938285608481856369c8f65760e11b958683523060048401523060248401528d60448401523360648401525af1948515610b7a578295610b59575b508290608485600d54169160405194859384928352306004840152306024840152600060448401523360648401525af1908115610b4e578891610b21575b5060405192608084018481106001600160401b03821117610b0b5760405288845260028385019489865282604082019716958688528360608301951697888652898d52600f875260408d20925183558b8301915115159060ff835491610100600160a81b03905160081b169216906affffffffffffffffffffff60a81b1617179055019151166bffffffffffffffffffffffff60a01b82541617905560135495600160401b871015610b0b57610ad6606096610ab789847f61afef2e121f71511fab2583c5ac49a5dc1d7a8ad8ad91c44c8465f073a73ac49b01601355612d3c565b90919082549060031b9160018060a01b03809116831b921b1916179055565b610ae16005546135e0565b60055584885260108252604088209060ff198254161790556040519384528301526040820152a180f35b634e487b7160e01b600052604160045260246000fd5b610b419150823d8411610b47575b610b398183612da1565b8101906136cc565b386109d5565b503d610b2f565b6040513d8a823e3d90fd5b83919550610b7390823d8411610b4757610b398183612da1565b9490610997565b6040513d84823e3d90fd5b6040516359f4180f60e11b8152600490fd5b5080fd5b503461034a57602036600319011261034a576080906001600160a01b039060409082610bc5612c78565b168152600f602052208054918060026001840154930154169160405193845260ff81161515602085015260081c1660408301526060820152f35b503461034a578060031936011261034a57602090604051908152f35b503461034a578060031936011261034a576020600854604051908152f35b503461034a57604036600319011261034a576040610c55612c8e565b9160043581526001602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b503461034a57604036600319011261034a5760043560243590610cb060ff600e5460a01c1615613620565b610cb8613676565b610cc4600654426136ff565b918184526020926011845260026040862001541015610f425762093a8080420642039060085490610cf582846134b4565b421192831593610f2c575b505050610f1a5760025460405163430c208160e01b8152336004820152602481018490526001600160a01b0393918416908581604481855afa908115610ee0578791610efd575b5015610eeb576040516368bcc3fd60e01b8152600481018390528581602481855afa908115610ee0578791610ea6575b506003811015610e9257610e80578086913b15610b975781809160446040518095819362d1e98760e21b83528860048401528960248401525af1918215610e73578692610e5c575b505060249360025416604051948580926339f890b560e21b82528660048301525afa928315610e51578593610e1a575b506011610e1394610e02600654426136ff565b928752526002604086200155613d1e565b6001815580f35b9380935084813d8311610e4a575b610e328183612da1565b81010312610e4557925191926011610def565b600080fd5b503d610e28565b6040513d87823e3d90fd5b610e67919250612d73565b61045057838538610dbf565b50604051903d90823e3d90fd5b6040516317a66f3760e01b8152600490fd5b634e487b7160e01b87526021600452602487fd5b90508581813d8311610ed9575b610ebd8183612da1565b81010312610ed557516003811015610ed55738610d77565b8680fd5b503d610eb3565b6040513d89823e3d90fd5b60405163390cdd9b60e21b8152600490fd5b610f149150863d8811610558576105488183612da1565b38610d47565b604051638bfc8f4360e01b8152600490fd5b610f379350016135ef565b421015388080610d00565b604051637c9a1cf960e01b8152600490fd5b503461034a57602036600319011261034a5760ff60036040602093600435815260118552200154166040519015158152f35b503461034a57602036600319011261034a57610fa0612c78565b610fa8612f72565b60018060a01b03166bffffffffffffffffffffffff60a01b600d541617600d5580f35b503461034a578060031936011261034a5760206040517f141f8f32ce6198eee741f695cec728bfd32d289f1acf73621fb303581000545e8152f35b503461034a57602036600319011261034a576020906001600160a01b039060019060409083611033612c78565b168152600f855220015460081c16604051908152f35b503461034a578060031936011261034a576020600754604051908152f35b503461034a57604036600319011261034a576106f9611084612c78565b61108c612f72565b60405163a9059cbb60e01b602082015233602480830191909152356044808301919091528152906110be606483612da1565b6001600160a01b03166139d7565b503461034a578060031936011261034a576020600354604051908152f35b503461034a57602036600319011261034a576020906040906001600160a01b03611112612c78565b168152600f83522054604051908152f35b503461034a57602036600319011261034a576040606091600435815260116020522080549060ff600360028301549201541690604051928352602083015215156040820152f35b503461034a57602036600319011261034a57611184612c78565b61118c612f72565b6001600160a01b039081168083526017602052604083205490919060ff16156112e957818352601760205260408320805460ff191690558291905b601892835493848210156112bb5782846111e084612d05565b929054600393841b1c16146112045750506111fd919293506135e0565b91906111c7565b60001995919493918087019081116112a75791610ab7916112286112349594612d05565b9054911b1c1691612d05565b8154918215611291577f92a36d261d412d062ad8096f1b691465373c5cd6132c0c62a7db23304dc126fa93602093019061128561127083612d05565b81549060018060a01b039060031b1b19169055565b555b604051908152a180f35b634e487b7160e01b600052603160045260246000fd5b634e487b7160e01b88526011600452602488fd5b5050507f92a36d261d412d062ad8096f1b691465373c5cd6132c0c62a7db23304dc126fa9150602090611287565b604051635a1303a760e01b8152600490fd5b503461034a57602036600319011261034a576004359060145482101561034a57602061033183612cb8565b503461034a57606036600319011261034a57600435906001600160401b0360243581811161161c5761135c903690600401612dd9565b9160443591821161034a573660238301121561034a5781600401359061138182612dc2565b9261138f6040519485612da1565b82845260209260248486019160051b83010191368311611618576024859101915b83831061160857505050506113c7600654426136ff565b8582526011835260026040832001541015610f425762093a80804206420390600854906113f482846134b4565b4211928315936115f2575b505050610f1a5760ff9161141a83600e5460a01c1615613620565b611422613676565b600a5442106115e057611437600654426136ff565b60025460405163430c208160e01b81523360048201526024810189905291966001600160a01b039290918316908481604481855afa9081156115d55786916115b8575b5015610eeb57815187510361058e578151600354106115a65783602491604051928380926339f890b560e21b82528d60048301525afa908115610e51578591611579575b50801561156757849885995b83518b101561154a57846114de8c866136eb565b51168752601086528760408820541615611538576114fc8b8a6136eb565b51156115265761151a611520916115138d8c6136eb565b51906134b4565b9a6135e0565b996114ca565b604051630c8685af60e01b8152600490fd5b60405163773836eb60e11b8152600490fd5b86928985610e13948d60118b838a5252600260408920015561371f565b60405163c5ba53b960e01b8152600490fd5b90508381813d831161159f575b6115908183612da1565b81010312610e455751386114be565b503d611586565b60405163cb9c2d7160e01b8152600490fd5b6115cf9150853d8711610558576105488183612da1565b3861147a565b6040513d88823e3d90fd5b604051633fd0090160e11b8152600490fd5b6115fd9350016135ef565b4210153880806113ff565b82358152918101918591016113b0565b8380fd5b8280fd5b503461034a57602036600319011261034a5760043561163d612f72565b8015611675576020817ffa4461b64d95d803511ec24ccf3d00fbf21a1f6c5000102a98e42b69b673b3ac92600355604051908152a180f35b6040516307707caf60e51b8152600490fd5b503461034a5761169636612e3f565b60025460405163430c208160e01b81523360048201526024810183905291929190602090829060449082906001600160a01b03165afa908115610e51578591611715575b501561170357610e13926116f660ff600e5460a01c1615613620565b6116fe613676565b613b9e565b604051637174172560e11b8152600490fd5b61172d915060203d8111610558576105488183612da1565b386116da565b503461034a578060031936011261034a5760206040517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b503461034a57602036600319011261034a577f28648db86b34efec68e71f9e1db4cb169caaad4c8132d6f4b9d4959042866b7560206004356117ae612f72565b80600855604051908152a180f35b503461034a57602036600319011261034a576117d6612c78565b6117de613175565b6001600160a01b03168082526010602052604082205460ff161561183d576020817fa21fc9f4c6fc68a99e154673b6692c3a1d92038cdbec927fe8842b12024953b9928452601082526040842060ff198154169055604051908152a180f35b6040516304b8da9960e51b8152600490fd5b503461034a57602036600319011261034a5760209060ff906040906001600160a01b0361187a612c78565b168152601784522054166040519015158152f35b503461034a57602036600319011261034a576118b260ff600e5460a01c1615613620565b6118ba612f72565b6118c2613676565b6006546118cf81426136ff565b6007541015612134576118e290426136ff565b60001990808201908111612120578252601260205260ff6040832054161561208c575b5060045460095490611919600435836134b4565b91601354808411612084575b50905b82821061198c575050806009556013541115611945576001815580f35b806004557f6d1c76d614228b523baa4dcd9539e2c713b54ff4ab3ff2d1627e7f6cd32be4426020611978600654426136ff565b8060075583600955604051908152a1610e13565b90919261199883612d3c565b9490549260018060a01b03848760031b1c168352600f60205260408320548015808015612070575b61206057670de0b6b3a76400008202918204670de0b6b3a764000014171561204c57816119ec916136ff565b91835b60145481101561201857611a0281612cb8565b60018060a01b0391549060031b1c168086526016602052670de0b6b3a7640000611a3086604089205461348b565b0480611a47575b5050611a42906135e0565b6119ef565b600c5460405163e170aec760e01b81526001600160a01b0360038d901b8b901c81166004830152909160209183916024918391165afa908115610b4e578891611ff9575b506001600160a01b03811615611fb4576006549063ffffffff80611aaf84426136ff565b169216809202918263ffffffff811603611fa05763ffffffff8316019063ffffffff8211611fa05763ffffffff8084168184160311611fa057611afe63ffffffff8085168185160316856136ff565b600b80546001600160a01b0316871493908415611cba57546001600160a01b0316803b15611cb6578c8091602460405180948193632e1a7d4d60e01b83528c60048401525af18015611cab57908d91611c97575b5050854710611c48576001600160a01b0383163b15611c4457858c949360a49363ffffffff8a94816040519a8b998a98630c26e85960e01b8a5260048a01526024890152166044870152166064850152608484015260018060a01b03165af18015610b4e578b92918991611c2b575b5050917f97e2814ff5d2cfd886169f6d9c27c303e27685bde750d0569fd838710da9941f91611c21611a4295945b604080516001600160a01b0360039590951b8e901c851681529490931660208501529183019190915281906060820190565b0390a19038611a37565b611c3791929350612d73565b610ed55789908738611bc1565b8b80fd5b60405162461bcd60e51b815260206004820152602160248201527f496e73756666696369656e74206e617469766520746f6b656e2062616c616e636044820152606560f81b6064820152608490fd5b611ca090612d73565b611c44578b38611b52565b6040513d8f823e3d90fd5b8c80fd5b509091939250841585611f20575b15611ebc5760405163095ea7b360e01b60208201526001600160a01b038516602482015260448082018790528152611d0b90611d05606482612da1565b876139d7565b604051636eb1769f60e11b81523060048201526001600160a01b03851660248201526020816044818a5afa908115611eb1579086918d91611e7c575b5010611e45576001600160a01b0384163b15611e4157604051630c26e85960e01b8152600481018790526000602482015263ffffffff9384166044820152921660648301526084820152888160a481836001600160a01b0387165af1801561055f578c9392918a91611e22575b505091611c21611a42959492611e1d7f97e2814ff5d2cfd886169f6d9c27c303e27685bde750d0569fd838710da9941f956040519063095ea7b360e01b602083015260018060a01b031660248201526000604482015260448152611e1781612d86565b856139d7565b611bef565b611e2f9192939450612d73565b611e3d57908a918838611db4565b8780fd5b8a80fd5b60405162461bcd60e51b815260206004820152600f60248201526e105c1c1c9bdd985b0819985a5b1959608a1b6044820152606490fd5b9150506020813d602011611ea9575b81611e9860209383612da1565b81010312610e455785905138611d47565b3d9150611e8b565b6040513d8e823e3d90fd5b60405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b50604051636eb1769f60e11b81523060048201526001600160a01b03851660248201526020816044818a5afa908115611f9457600091611f62575b5015611cc8565b906020823d602011611f8c575b81611f7c60209383612da1565b8101031261034a57505138611f5b565b3d9150611f6f565b6040513d6000823e3d90fd5b634e487b7160e01b8a52601160045260248afd5b60405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207265776172646572206164647265737300000000000000006044820152606490fd5b612012915060203d602011610b4757610b398183612da1565b38611a8b565b5060039690961b9390931c6001600160a01b0316825250600f602052604081208190559291612046906135e0565b90611928565b634e487b7160e01b84526011600452602484fd5b50509250926120469194506135e0565b50601060205260ff604086205416156119c0565b925038611925565b600e5482906001600160a01b0316803b15610b97578180916004604051809481936301f564d960e71b83525af18015610b7a5761210c575b50506120d2600654426136ff565b9081019081116120f8578152601260205260408120805460ff1916600117905538611905565b634e487b7160e01b82526011600452602482fd5b61211590612d73565b610b975781386120c4565b634e487b7160e01b83526011600452602483fd5b60405163ec76655760e01b8152600490fd5b503461034a57602036600319011261034a576004359060185482101561034a57602061033183612d05565b503461034a57602036600319011261034a57600435801515809103610b9757612198612f72565b600e805460ff60a01b191660a09290921b60ff60a01b1691909117905580f35b503461034a578060031936011261034a576020600654604051908152f35b503461034a57602036600319011261034a576121f0612c78565b6121f8612f72565b6001600160a01b0381168083526017602052604083205490919060ff1661227757818352601760205260408320805460ff1916600117905560185491600160401b831015610b0b57611287602092610ab78560017f1761ea52e8e8eaef6dd99a3c7fe7b2494372d7a8fd6d5560ea0dce99b2e706769701601855612d05565b60405163630b374b60e01b8152600490fd5b503461034a578060031936011261034a57600b546040516001600160a01b039091168152602090f35b503461034a57602036600319011261034a576122cc612c78565b6122d4612f72565b6001600160a01b039081168083526015602052604083205490919060ff16156112e957818352601560205260408320805460ff191690558291905b601492835493848210156123b857828461232884612cb8565b929054600393841b1c161461234c575050612345919293506135e0565b919061230f565b60001995919493918087019081116112a75791610ab79161237061237c9594612cb8565b9054911b1c1691612cb8565b8154918215611291577f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf500993602093019061128561127083612cb8565b5050507f66257bcef574219c04f7c05f7a1c78d599da10491294c92a5805c48b4cdf50099150602090611287565b503461034a57602036600319011261034a576020906040906001600160a01b0361240e612c78565b168152601683522054604051908152f35b503461034a57604036600319011261034a57612439612c8e565b336001600160a01b03821603612455576106f990600435613415565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b503461034a578060031936011261034a576020600a54604051908152f35b503461034a57602036600319011261034a576004356124f760ff600e5460a01c1615613620565b612503600654426136ff565b818352601160205260026040842001541015610f425762093a808042064203906008549061253182846134b4565b4211928315936125c4575b505050610f1a5760025460405163430c208160e01b81523360048201526024810183905290602090829060449082906001600160a01b03165afa9081156125b957839161259b575b501561170357610e1390612596613676565b613ded565b6125b3915060203d8111610558576105488183612da1565b38612584565b6040513d85823e3d90fd5b6125cf9350016135ef565b42101538808061253c565b503461034a57604036600319011261034a576004356125f7612c8e565b818352600160205261260f60016040852001546132bc565b8183526001602052604083209060018060a01b0316908160005260205260ff604060002054161561263e578280f35b818352600160205260408320816000526020526040600020600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8480a438808280f35b503461034a57602036600319011261034a5760209060ff906001906040906001600160a01b036126bb612c78565b168152600f8552200154166040519015158152f35b503461034a57604036600319011261034a57600160406126ee612c8e565b926004358152601160205220019060018060a01b03166000526020526020604060002054604051908152f35b503461034a578060031936011261034a57602060ff600e5460a01c166040519015158152f35b503461034a57602036600319011261034a57600160406020926004358152828452200154604051908152f35b503461034a57602080600319360112610b97576004359061279560ff600e5460a01c1615613620565b61279d613676565b6127a9600654426136ff565b8284526011825260026040852001541015610f425762093a80804206420390600854906127d682846134b4565b4211928315936129bd575b505050610f1a5760025460405163430c208160e01b8152336004820152602481018490526001600160a01b0391821693908381604481885afa9081156115d55786916129a0575b5015610eeb57849060405163055c87a560e31b8152816004820152604081602481895afa9081156125b957839161292a575b505194803b1561161c5760248392604051948593849263c07b5b0b60e01b845260048401525af18015610e5157612914575b508160249160025416604051928380926339f890b560e21b82528760048301525afa9182156129095784926128db575b5050806128cd5750610e1390613ded565b6128d691613d1e565b610e13565b90809250813d8311612902575b6128f28183612da1565b81010312610e45575138806128bc565b503d6128e8565b6040513d86823e3d90fd5b602491946129228492612d73565b94915061288c565b919250506040813d604011612998575b8161294760409383612da1565b810103126129945760405190604082018281106001600160401b038211176129805760405280518252840151848201528591903861285a565b634e487b7160e01b88526041600452602488fd5b8580fd5b3d915061293a565b6129b79150843d8611610558576105488183612da1565b38612828565b6129c89350016135ef565b4210153880806127e1565b503461034a57602036600319011261034a576129ed612c78565b6129f5612f72565b6001600160a01b0381168083526015602052604083205490919060ff1661227757601454600160401b811015612a7c5791612a5d602092610ab78560017ff3e4c2c64e71e6ba2eaab9a599bced62f9eb91d2cda610bf41aa8c80ff2cf8269701601455612cb8565b808452601582526040808520805460ff1916600117905551908152a180f35b634e487b7160e01b84526041600452602484fd5b503461034a578060031936011261034a57604051809182601854808452602080940190601884527fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e935b85828210612af15750505061081f92500383612da1565b85546001600160a01b0316845260019586019588955093019201612ada565b503461034a57602036600319011261034a5760209060ff906040906001600160a01b03612b3b612c78565b168152601084522054166040519015158152f35b503461034a578060031936011261034a576020600454604051908152f35b503461034a57602036600319011261034a5761067a6004604061068e938235815260116020522001613cc6565b503461034a57602036600319011261034a57612bb4612f72565b600435600a5580f35b503461034a578060031936011261034a57600e546040516001600160a01b039091168152602090f35b905034610b97576020366003190112610b975760043563ffffffff60e01b811680910361161c5760209250637965db0b60e01b8114908115612c2a575b5015158152f35b6301ffc9a760e01b14905038612c23565b90815180825260208080930193019160005b828110612c5b575050505090565b83516001600160a01b031685529381019392810192600101612c4d565b600435906001600160a01b0382168203610e4557565b602435906001600160a01b0382168203610e4557565b35906001600160a01b0382168203610e4557565b601454811015612cef5760146000527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec0190600090565b634e487b7160e01b600052603260045260246000fd5b601854811015612cef5760186000527fb13d2d76d1f4b7be834882e410b3e3a8afaf69f83600ae24db354391d2378d2e0190600090565b601354811015612cef5760136000527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0900190600090565b6001600160401b038111610b0b57604052565b608081019081106001600160401b03821117610b0b57604052565b90601f801991011681019081106001600160401b03821117610b0b57604052565b6001600160401b038111610b0b5760051b60200190565b81601f82011215610e4557803591612df083612dc2565b92612dfe6040519485612da1565b808452602092838086019260051b820101928311610e45578301905b828210612e28575050505090565b838091612e3484612ca4565b815201910190612e1a565b906060600319830112610e45576001600160401b03600435818111610e455783612e6b91600401612dd9565b926024803592808411610e455782602385011215610e45578360040135612e9181612dc2565b94612e9f6040519687612da1565b818652602094848688019360051b83010194818611610e4557808301935b868510612ed35750505050505050509060443590565b8435868111610e45578891612eed85858594890101612dd9565b815201940193612ebd565b9181601f84011215610e45578235916001600160401b038311610e45576020808501948460051b010111610e4557565b6040600319820112610e45576001600160401b0391600435838111610e455782612f5491600401612ef8565b93909392602435918211610e4557612f6e91600401612ef8565b9091565b3360009081527f50efbde2d46c37e9785f1791697f77e94bb7b701e19f1930a668820722d3769460209081526040808320549092906001907fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217759060ff1615612fdb575050505050565b612fe4336134d2565b90855192612ff184612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613107575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b8401917001034b99036b4b9b9b4b733903937b6329607d1b6037840152518093868401906133c6565b01036028810185520183612da1565b5162461bcd60e51b8152918291600483016133e9565b0390fd5b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a61313785886134c1565b5360041c9280156120f85760001901919061301e565b634e487b7160e01b83526032600452602483fd5b634e487b7160e01b81526032600452602490fd5b3360009081527fcacae11c3e803b9c57e53bab46d0843c5e8225f3d7f2d3156db434e191c2d6f660209081526040808320549092906001907f141f8f32ce6198eee741f695cec728bfd32d289f1acf73621fb303581000545e9060ff16156131de575050505050565b6131e7336134d2565b908551926131f484612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613276575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6132a685886134c1565b5360041c9280156120f857600019019190613221565b6000818152600190602091808352604093848320338452845260ff8584205416156132e8575050505050565b6132f1336134d2565b908551926132fe84612d86565b60428452858401946060368737845115613161576030865384518210156131615790607860218601536041915b818311613380575050506130c5576130739385936130ab9361309c6048946130c19951988576020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8b9788015282519283916037890191016133c6565b909192600f8116601081101561314d576f181899199a1a9b1b9c1cb0b131b232b360811b901a6133b085886134c1565b5360041c9280156120f85760001901919061332b565b60005b8381106133d95750506000910152565b81810151838201526020016133c9565b6040916020825261340981518092816020860152602086860191016133c6565b601f01601f1916010190565b906000918083526001602052604083209160018060a01b03169182845260205260ff60408420541661344657505050565b8083526001602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b8181029291811591840414171561349e57565b634e487b7160e01b600052601160045260246000fd5b9190820180921161349e57565b908151811015612cef570160200190565b60405190606082018281106001600160401b03821117610b0b57604052602a8252602082016040368237825115612cef57603090538151600190811015612cef57607860218401536029905b80821161357257505061352e5790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f811660108110156135cb576f181899199a1a9b1b9c1cb0b131b232b360811b901a6135a184866134c1565b5360041c9180156135b657600019019061351e565b60246000634e487b7160e01b81526011600452fd5b60246000634e487b7160e01b81526032600452fd5b600019811461349e5760010190565b9190820391821161349e57565b9190811015612cef5760051b0190565b356001600160a01b0381168103610e455790565b1561362757565b60405162461bcd60e51b815260206004820152600f60248201526e10dbdb9d1c9858dd081c185d5cd959608a1b6044820152606490fd5b90816020910312610e4557518015158103610e455790565b600260005414613687576002600055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b90816020910312610e4557516001600160a01b0381168103610e455790565b8051821015612cef5760209160051b010190565b8115613709570490565b634e487b7160e01b600052601260045260246000fd5b92949390919483600052601160205260406000209261373d85613ded565b6004549560005b82518110156138a25788906137808661377b87613775856001600160a01b0361376d828c6136eb565b5116976136eb565b5161348b565b6136ff565b6000838152600f602052604090206001015490929060081c6001600160a01b0316803b15610e45576000809160448c60405194859384926332d91c4d60e11b84528a600485015260248401525af18015611f9457613893575b506000818152600f60205260409020600201546001600160a01b031692833b15610e45576000809460448c60405197889384926332d91c4d60e11b845287600485015260248401525af1918215611f945761387f9461387793613884575b5080600052600f60205260406000206138518382546134b4565b905560005260018901602052604060002061386d8282546134b4565b90556004546134b4565b6004556135e0565b613744565b61388d90612d73565b38613837565b61389c90612d73565b386137d9565b50969392509394905060038201600160ff19825416179055600482018451906001600160401b038211610b0b57600160401b8211610b0b5780548282558083106139ae575b506020860190600052602060002060005b8381106139915750505050613912613919916004546135ef565b82546134b4565b905561393060405192604084526040840190612c3b565b82810360208401526020808351928381520192019060005b81811061397b5750505090807fcd2605b2299581c1cf98aa371c3733db302fa37a6cfdd86dbf7dadf61aae551e920390a2565b8251845260209384019390920191600101613948565b82516001600160a01b0316818301556020909201916001016138f8565b8160005282602060002091820191015b8181106139cb57506138e7565b600081556001016139be565b60408051908101916001600160a01b03166001600160401b03831182841017610b0b57613a46926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1613a40613ace565b91613b0d565b805190828215928315613ab6575b50505015613a5f5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b613ac6935082018101910161365e565b388281613a54565b3d15613b08573d906001600160401b038211610b0b5760405191613afc601f8201601f191660200184612da1565b82523d6000602084013e565b606090565b91929015613b6f5750815115613b21575090565b3b15613b2a5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015613b825750805190602001fd5b60405162461bcd60e51b81529081906130c190600483016133e9565b9081519160005b838110613bb3575050505050565b6001600160a01b03613bc582846136eb565b5116613bd182856136eb565b5190803b15610e45576000613c13926040928983855180978195829463a44d113f60e01b84523360048501526024840152606060448401526064830190612c3b565b03925af1908115613c44575090613c309291613c35575b506135e0565b613ba5565b613c3e90612d73565b38613c2a565b513d6000823e3d90fd5b60405190601354808352826020918282019060136000527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090936000905b828210613ca357505050613ca192500383612da1565b565b85546001600160a01b031684526001958601958895509381019390910190613c8b565b9060405191828154918282526020928383019160005283600020936000905b828210613cfb57505050613ca192500383612da1565b85546001600160a01b031684526001958601958895509381019390910190613ce5565b91906000908382526020936011855260409283812091613d4060048401613cc6565b91825193613d4d85612dc2565b94613d5a88519687612da1565b808652613d69601f1991612dc2565b01368a870137819682600180809401905b613d8f575b5050505050613ca195965061371f565b909192939886518a1015613de757613ddd90613dd790838e6001600160a01b03613db98f8d6136eb565b511687525285852054613dcc8d8c6136eb565b526115138c8b6136eb565b996135e0565b9392919083613d7a565b98613d7f565b60009080825260206011815260409283812093600293848601546007541091613e14613c4e565b91849060019788948593848c01945b613e8f575b50505050505050613e7a575b808455600484019182549282815583613e5a575b505050505060030160ff198154169055565b82528120918201915b828110613e705780613e48565b8181558301613e63565b613e8784546004546135ef565b600455613e34565b8151811015613fe2576001600160a01b0380613eab83856136eb565b511690818b52868c528b888c2054918c83613ed5575b5050505050613ecf906135e0565b86613e23565b96818b829a9b9c9d949596979899600f809752209384015460081c1692015416813b15611cb6578c908b51908a63278afc8b60e21b9283815284816004978a89830152602494858301528160449889925af18015613fd457613fc5575b50813b15613fc157918f8094928f8997958f9151988997889687528601528401525af18015613fb757918b8f9b9a999897969492613ecf9694613fa8575b50613f8c575b50508a52858b52898781205590388b818c613ec1565b828d528d52613f9f898d209182546135ef565b90553880613f76565b613fb190612d73565b38613f70565b8a513d8e823e3d90fd5b8f80fd5b613fce90612d73565b38613f32565b50508f8e51903d90823e3d90fd5b613e2856fea2646970667358221220036801c05cce1be840deec2f639c2c06746c2883d6547e37cfe6c8aeacd69fe464736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fa62b5962a7923a2910f945268aa65c943d131e9000000000000000000000000acc15dc74880c9944775448304b263d191c6077f00000000000000000000000005094f278f6da7c46aa0500c00b208e8c4899909000000000000000000000000704ba24f82ffa73b605b01e9f536d320f2d635590000000000000000000000006073675fa8e365c8f4fc402c930b50d8405a71ff
-----Decoded View---------------
Arg [0] : _veStellaToken (address): 0xfa62B5962a7923A2910F945268AA65C943D131e9
Arg [1] : _WGLMR (address): 0xAcc15dC74880C9944775448304B263D191c6077F
Arg [2] : _rewardRegistryAddress (address): 0x05094F278f6Da7C46AA0500c00b208E8C4899909
Arg [3] : _incentiveManagerFactory (address): 0x704Ba24F82fFa73b605B01e9F536d320f2D63559
Arg [4] : _minter (address): 0x6073675fA8E365C8f4Fc402c930B50d8405a71Ff
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000fa62b5962a7923a2910f945268aa65c943d131e9
Arg [1] : 000000000000000000000000acc15dc74880c9944775448304b263d191c6077f
Arg [2] : 00000000000000000000000005094f278f6da7c46aa0500c00b208e8c4899909
Arg [3] : 000000000000000000000000704ba24f82ffa73b605b01e9f536d320f2d63559
Arg [4] : 0000000000000000000000006073675fa8e365c8f4fc402c930b50d8405a71ff
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in GLMR
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.