Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 32 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw Unused ... | 18753417 | 335 days ago | IN | 0 ETH | 0.0015372 | ||||
Withdraw Unused ... | 18753417 | 335 days ago | IN | 0 ETH | 0.00142126 | ||||
Withdraw Unused ... | 18584602 | 358 days ago | IN | 0 ETH | 0.00147121 | ||||
Add Multiple Mer... | 18433482 | 379 days ago | IN | 0 ETH | 0.00313532 | ||||
Withdraw Unused ... | 18433442 | 379 days ago | IN | 0 ETH | 0.00134953 | ||||
Close Quest Peri... | 18432981 | 379 days ago | IN | 0 ETH | 0.01220871 | ||||
Withdraw Unused ... | 18383769 | 386 days ago | IN | 0 ETH | 0.00049772 | ||||
Add Multiple Mer... | 18383149 | 386 days ago | IN | 0 ETH | 0.00067893 | ||||
Close Quest Peri... | 18383131 | 386 days ago | IN | 0 ETH | 0.00130462 | ||||
Add Multiple Mer... | 18332841 | 393 days ago | IN | 0 ETH | 0.00072624 | ||||
Close Quest Peri... | 18332832 | 393 days ago | IN | 0 ETH | 0.001297 | ||||
Create Quest | 18283539 | 400 days ago | IN | 0 ETH | 0.0037849 | ||||
Withdraw Unused ... | 18283511 | 400 days ago | IN | 0 ETH | 0.00044014 | ||||
Withdraw Unused ... | 18283506 | 400 days ago | IN | 0 ETH | 0.00042504 | ||||
Withdraw Unused ... | 18283503 | 400 days ago | IN | 0 ETH | 0.00041152 | ||||
Add Multiple Mer... | 18282966 | 400 days ago | IN | 0 ETH | 0.00119542 | ||||
Close Quest Peri... | 18282949 | 400 days ago | IN | 0 ETH | 0.00248142 | ||||
Add Multiple Mer... | 18232885 | 407 days ago | IN | 0 ETH | 0.00161399 | ||||
Close Quest Peri... | 18232740 | 407 days ago | IN | 0 ETH | 0.00484651 | ||||
Create Quest | 18226327 | 408 days ago | IN | 0 ETH | 0.00658246 | ||||
Add Multiple Mer... | 18184980 | 414 days ago | IN | 0 ETH | 0.0035582 | ||||
Close Quest Peri... | 18183141 | 414 days ago | IN | 0 ETH | 0.00725317 | ||||
Create Quest | 18168105 | 417 days ago | IN | 0 ETH | 0.00313096 | ||||
Create Quest | 18129010 | 422 days ago | IN | 0 ETH | 0.0104199 | ||||
Create Quest | 18129004 | 422 days ago | IN | 0 ETH | 0.0077121 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
LightQuestBoard
Compiler Version
v0.8.10+commit.fc410830
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
//██████╗ █████╗ ██╗ █████╗ ██████╗ ██╗███╗ ██╗ //██╔══██╗██╔══██╗██║ ██╔══██╗██╔══██╗██║████╗ ██║ //██████╔╝███████║██║ ███████║██║ ██║██║██╔██╗ ██║ //██╔═══╝ ██╔══██║██║ ██╔══██║██║ ██║██║██║╚██╗██║ //██║ ██║ ██║███████╗██║ ██║██████╔╝██║██║ ╚████║ //╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝ // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./oz/interfaces/IERC20.sol"; import "./oz/libraries/SafeERC20.sol"; import "./utils/Owner.sol"; import "./oz/utils/ReentrancyGuard.sol"; import "./MultiMerkleDistributor.sol"; import "./interfaces/IGaugeController.sol"; import "./utils/Errors.sol"; /** @title Warden Light Quest Board */ /// @author Paladin /* Main contract, holding all the Quests data & ressources Allowing users to add/update Quests And the managers to update Quests to the next period & trigger the rewards for closed periods Version of the Quest Board only fetching the votes of 1 designated address(the GAUGE_VOTER) to calculate rewards based on the deisgnated address votes (ex: the Convex Voter Proxy) */ contract LightQuestBoard is Owner, ReentrancyGuard { using SafeERC20 for IERC20; /** @notice Address of the Curve Gauge Controller */ address public immutable GAUGE_CONTROLLER; /** @notice Address of the Voter used to count rewards */ address public immutable GAUGE_VOTER; /** @notice Seconds in a Week */ uint256 private constant WEEK = 604800; /** @notice 1e18 scale */ uint256 private constant UNIT = 1e18; /** @notice Max BPS value (100%) */ uint256 private constant MAX_BPS = 10000; /** @notice State of each Period for each Quest */ enum PeriodState { ZERO, ACTIVE, CLOSED, DISTRIBUTED } // All Periods are ACTIVE at creation since they voters from past periods are also accounted for the future period /** @notice Struct for a Period of a Quest */ struct QuestPeriod { // Total reward amount that can be distributed for that period uint256 rewardAmountPerPeriod; // Amount of reward for each vote (for 1 veCRV) uint256 rewardPerVote; // Tartget Bias for the Gauge uint256 objectiveVotes; // Amount of reward to distribute, at period closing uint256 rewardAmountDistributed; // Amount not distributed, for Quest creator to redeem uint256 withdrawableAmount; // Timestamp of the Period start uint48 periodStart; // Current state of the Period PeriodState currentState; } /** @notice Struct holding the parameters of the Quest common for all periods */ struct Quest { // Address of the Quest creator (caller of createQuest() method) address creator; // Address of the ERC20 used for rewards address rewardToken; // Address of the target Gauge address gauge; // Total number of periods for the Quest uint48 duration; // Timestamp where the 1st QuestPeriod starts uint48 periodStart; // Total amount of rewards paid for this Quest // If changes were made to the parameters of this Quest, this will account // any added reward amounts uint256 totalRewardAmount; } /** @notice ID for the next Quest to be created */ uint256 public nextID; /** @notice List of Quest (indexed by ID) */ // ID => Quest mapping(uint256 => Quest) public quests; /** @notice List of timestamp periods the Quest is active in */ // QuestID => Periods (timestamps) mapping(uint256 => uint48[]) public questPeriods; /** @notice Mapping of all QuestPeriod struct for each period of each Quest */ // QuestID => period => QuestPeriod mapping(uint256 => mapping(uint256 => QuestPeriod)) public periodsByQuest; /** @notice All the Quests present in this period */ // period => array of Quest mapping(uint256 => uint256[]) public questsByPeriod; /** @notice Mapping of Distributors used by each Quest to send rewards */ // ID => Distributor mapping(uint256 => address) public questDistributors; /** @notice Platform fees ratio (in BPS) */ uint256 public platformFee = 400; /** @notice Minimum Objective required */ uint256 public minObjective; /** @notice Address of the Chest to receive platform fees */ address public questChest; /** @notice Address of the reward Distributor contract */ address public distributor; /** @notice Mapping of addresses allowed to call manager methods */ mapping(address => bool) approvedManagers; /** @notice Whitelisted tokens that can be used as reward tokens */ mapping(address => bool) public whitelistedTokens; /** @notice Min rewardPerVote per token (to avoid spam creation of useless Quest) */ mapping(address => uint256) public minRewardPerVotePerToken; /** @notice Boolean, true if the cotnract was killed, stopping main user functions */ bool public isKilled; /** @notice Timestam pwhen the contract was killed */ uint256 public kill_ts; /** @notice Delay where contract can be unkilled */ uint256 public constant KILL_DELAY = 2 * 604800; //2 weeks // Events /** @notice Event emitted when a new Quest is created */ event NewQuest( uint256 indexed questID, address indexed creator, address indexed gauge, address rewardToken, uint48 duration, uint256 startPeriod, uint256 objectiveVotes, uint256 rewardPerVote ); /** @notice Event emitted when rewards of a Quest are increased */ event IncreasedQuestReward(uint256 indexed questID, uint256 indexed updatePeriod, uint256 newRewardPerVote, uint256 addedRewardAmount); /** @notice Event emitted when the Quest objective bias is increased */ event IncreasedQuestObjective(uint256 indexed questID, uint256 indexed updatePeriod, uint256 newObjective, uint256 addedRewardAmount); /** @notice Event emitted when the Quest duration is extended */ event IncreasedQuestDuration(uint256 indexed questID, uint256 addedDuration, uint256 addedRewardAmount); /** @notice Event emitted when Quest creator withdraw undistributed rewards */ event WithdrawUnusedRewards(uint256 indexed questID, address recipient, uint256 amount); /** @notice Event emitted when a Period is Closed */ event PeriodClosed(uint256 indexed questID, uint256 indexed period); /** @notice Event emitted when a new reward token is whitelisted */ event WhitelistToken(address indexed token, uint256 minRewardPerVote); event UpdateRewardToken(address indexed token, uint256 newMinRewardPerVote); /** @notice Event emitted when the contract is killed */ event Killed(uint256 killTime); /** @notice Event emitted when the contract is unkilled */ event Unkilled(uint256 unkillTime); /** @notice Event emitted when the Quest creator withdraw all unused funds (if the contract was killed) */ event EmergencyWithdraw(uint256 indexed questID, address recipient, uint256 amount); event InitDistributor(address distributor); event ApprovedManager(address indexed manager); event RemovedManager(address indexed manager); event ChestUpdated(address oldChest, address newChest); event DistributorUpdated(address oldDistributor, address newDistributor); event PlatformFeeUpdated(uint256 oldfee, uint256 newFee); event MinObjectiveUpdated(uint256 oldMinObjective, uint256 newMinObjective); // Modifiers /** @notice Check the caller is either the admin or an approved manager */ modifier onlyAllowed(){ if(!approvedManagers[msg.sender] && msg.sender != owner()) revert Errors.CallerNotAllowed(); _; } /** @notice Check that contract was not killed */ modifier isAlive(){ if(isKilled) revert Errors.Killed(); _; } // Constructor constructor(address _gaugeController, address _gaugeVoter, address _chest){ if(_gaugeController == address(0)) revert Errors.ZeroAddress(); if(_chest == address(0)) revert Errors.ZeroAddress(); if(_gaugeController == _chest) revert Errors.SameAddress(); GAUGE_CONTROLLER = _gaugeController; GAUGE_VOTER = _gaugeVoter; questChest = _chest; minObjective = 1000 * UNIT; } // View Functions /** * @notice Returns the current Period for the contract * @dev Returns the current Period for the contract */ function getCurrentPeriod() public view returns(uint256) { return (block.timestamp / WEEK) * WEEK; } /** * @notice Returns the list of all Quest IDs active on a given period * @dev Returns the list of all Quest IDs active on a given period * @param period Timestamp of the period * @return uint256[] : Quest IDs for the period */ function getQuestIdsForPeriod(uint256 period) external view returns(uint256[] memory) { period = (period / WEEK) * WEEK; return questsByPeriod[period]; } /** * @notice Returns all periods for a Quest * @dev Returns all period timestamps for a Quest ID * @param questId ID of the Quest * @return uint256[] : List of period timestamps */ function getAllPeriodsForQuestId(uint256 questId) external view returns(uint48[] memory) { return questPeriods[questId]; } /** * @notice Returns all QuestPeriod of a given Quest * @dev Returns all QuestPeriod of a given Quest ID * @param questId ID of the Quest * @return QuestPeriod[] : list of QuestPeriods */ function getAllQuestPeriodsForQuestId(uint256 questId) external view returns(QuestPeriod[] memory) { uint256 nbPeriods = questPeriods[questId].length; QuestPeriod[] memory periods = new QuestPeriod[](nbPeriods); for(uint256 i; i < nbPeriods;){ periods[i] = periodsByQuest[questId][questPeriods[questId][i]]; unchecked{ ++i; } } return periods; } /** * @dev Returns the number of periods to come for a given Quest * @param questID ID of the Quest * @return uint : remaining duration (non active periods) */ function _getRemainingDuration(uint256 questID) internal view returns(uint256) { // Since we have the current period, the start period for the Quest, and each period is 1 WEEK // We can find the number of remaining periods in the Quest simply by dividing the remaining time between // currentPeriod and the last QuestPeriod start by a WEEK. // If the current period is the last period of the Quest, we want to return 0 if(questPeriods[questID].length == 0) revert Errors.EmptyQuest(); uint256 lastPeriod = questPeriods[questID][questPeriods[questID].length - 1]; uint256 currentPeriod = getCurrentPeriod(); return lastPeriod < currentPeriod ? 0: (lastPeriod - currentPeriod) / WEEK; } // Functions struct CreateVars { address creator; uint256 rewardPerPeriod; uint256 nextPeriod; } /** * @notice Creates a new Quest * @dev Creates a new Quest struct, and QuestPeriods for the Quest duration * @param gauge Address of the Gauge targeted by the Quest * @param rewardToken Address of the reward token * @param duration Duration (in number of periods) of the Quest * @param objective Target bias to reach (equivalent to amount of veCRV in wei to reach) * @param rewardPerVote Amount of reward per veCRV (in wei) * @param totalRewardAmount Total amount of rewards for the whole Quest (in wei) * @param feeAmount Platform fees amount (in wei) * @return uint256 : ID of the newly created Quest */ function createQuest( address gauge, address rewardToken, uint48 duration, uint256 objective, uint256 rewardPerVote, uint256 totalRewardAmount, uint256 feeAmount ) external isAlive nonReentrant returns(uint256) { if(distributor == address(0)) revert Errors.NoDistributorSet(); // Local memory variables CreateVars memory vars; vars.creator = msg.sender; // Check all parameters if(gauge == address(0) || rewardToken == address(0)) revert Errors.ZeroAddress(); if(IGaugeController(GAUGE_CONTROLLER).gauge_types(gauge) < 0) revert Errors.InvalidGauge(); if(!whitelistedTokens[rewardToken]) revert Errors.TokenNotWhitelisted(); if(duration == 0) revert Errors.IncorrectDuration(); if(objective < minObjective) revert Errors.ObjectiveTooLow(); if(rewardPerVote == 0 || totalRewardAmount == 0 || feeAmount == 0) revert Errors.NullAmount(); if(rewardPerVote < minRewardPerVotePerToken[rewardToken]) revert Errors.RewardPerVoteTooLow(); // Verifiy the given amounts of reward token are correct vars.rewardPerPeriod = (objective * rewardPerVote) / UNIT; if((vars.rewardPerPeriod * duration) != totalRewardAmount) revert Errors.IncorrectTotalRewardAmount(); if((totalRewardAmount * platformFee)/MAX_BPS != feeAmount) revert Errors.IncorrectFeeAmount(); // Pull all the rewards in this contract IERC20(rewardToken).safeTransferFrom(vars.creator, address(this), totalRewardAmount); // And transfer the fees from the Quest creator to the Chest contract IERC20(rewardToken).safeTransferFrom(vars.creator, questChest, feeAmount); // Quest will start on next period vars.nextPeriod = getCurrentPeriod() + WEEK; // Get the ID for that new Quest and increment the nextID counter uint256 newQuestID = nextID; unchecked{ ++nextID; } // Fill the Quest struct data quests[newQuestID].creator = vars.creator; quests[newQuestID].rewardToken = rewardToken; quests[newQuestID].gauge = gauge; quests[newQuestID].duration = duration; quests[newQuestID].totalRewardAmount = totalRewardAmount; quests[newQuestID].periodStart = safe48(vars.nextPeriod); uint48[] memory _periods = new uint48[](duration); //Set the current Distributor as the one to receive the rewards for users for that Quest questDistributors[newQuestID] = distributor; // Iterate on periods based on Quest duration uint256 periodIterator = vars.nextPeriod; for(uint256 i; i < duration;){ // Add the Quest on the list of Quests active on the period questsByPeriod[periodIterator].push(newQuestID); // And add the period in the list of periods of the Quest _periods[i] = safe48(periodIterator); periodsByQuest[newQuestID][periodIterator].periodStart = safe48(periodIterator); periodsByQuest[newQuestID][periodIterator].objectiveVotes = objective; periodsByQuest[newQuestID][periodIterator].rewardPerVote = rewardPerVote; periodsByQuest[newQuestID][periodIterator].rewardAmountPerPeriod = vars.rewardPerPeriod; periodsByQuest[newQuestID][periodIterator].currentState = PeriodState.ACTIVE; // Rest of the struct shoud laready have the correct base data: // rewardAmountDistributed => 0 // withdrawableAmount => 0 periodIterator = ((periodIterator + WEEK) / WEEK) * WEEK; unchecked{ ++i; } } // Write the array of period timestamp of that Quest in storage questPeriods[newQuestID] = _periods; // Add that Quest & the reward token in the Distributor if(!MultiMerkleDistributor(distributor).addQuest(newQuestID, rewardToken)) revert Errors.DisitributorFail(); emit NewQuest( newQuestID, vars.creator, gauge, rewardToken, duration, vars.nextPeriod, objective, rewardPerVote ); return newQuestID; } /** * @notice Increases the duration of a Quest * @dev Adds more QuestPeriods and extends the duration of a Quest * @param questID ID of the Quest * @param addedDuration Number of period to add * @param addedRewardAmount Amount of reward to add for the new periods (in wei) * @param feeAmount Platform fees amount (in wei) */ function increaseQuestDuration( uint256 questID, uint48 addedDuration, uint256 addedRewardAmount, uint256 feeAmount ) external isAlive nonReentrant { if(questID >= nextID) revert Errors.InvalidQuestID(); if(msg.sender != quests[questID].creator) revert Errors.CallerNotAllowed(); if(addedRewardAmount == 0 || feeAmount == 0) revert Errors.NullAmount(); if(addedDuration == 0) revert Errors.IncorrectAddDuration(); //We take data from the last period of the Quest to account for any other changes in the Quest parameters if(questPeriods[questID].length == 0) revert Errors.EmptyQuest(); uint256 lastPeriod = questPeriods[questID][questPeriods[questID].length - 1]; if(lastPeriod < getCurrentPeriod()) revert Errors.ExpiredQuest(); // Check that the given amounts are correct uint rewardPerPeriod = periodsByQuest[questID][lastPeriod].rewardAmountPerPeriod; if((rewardPerPeriod * addedDuration) != addedRewardAmount) revert Errors.IncorrectAddedRewardAmount(); if((addedRewardAmount * platformFee)/MAX_BPS != feeAmount) revert Errors.IncorrectFeeAmount(); address rewardToken = quests[questID].rewardToken; // Pull all the rewards in this contract IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), addedRewardAmount); // And transfer the fees from the Quest creator to the Chest contract IERC20(rewardToken).safeTransferFrom(msg.sender, questChest, feeAmount); uint256 periodIterator = ((lastPeriod + WEEK) / WEEK) * WEEK; // Update the Quest struct with added reward admounts & added duration quests[questID].totalRewardAmount += addedRewardAmount; quests[questID].duration += addedDuration; uint256 objective = periodsByQuest[questID][lastPeriod].objectiveVotes; uint256 rewardPerVote = periodsByQuest[questID][lastPeriod].rewardPerVote; // Add QuestPeriods for the new added duration for(uint256 i; i < addedDuration;){ questsByPeriod[periodIterator].push(questID); questPeriods[questID].push(safe48(periodIterator)); periodsByQuest[questID][periodIterator].periodStart = safe48(periodIterator); periodsByQuest[questID][periodIterator].objectiveVotes = objective; periodsByQuest[questID][periodIterator].rewardPerVote = rewardPerVote; periodsByQuest[questID][periodIterator].rewardAmountPerPeriod = rewardPerPeriod; periodsByQuest[questID][periodIterator].currentState = PeriodState.ACTIVE; // Rest of the struct shoud laready have the correct base data: // rewardAmountDistributed => 0 // redeemableAmount => 0 periodIterator = ((periodIterator + WEEK) / WEEK) * WEEK; unchecked{ ++i; } } emit IncreasedQuestDuration(questID, addedDuration, addedRewardAmount); } /** * @notice Increases the reward per votes for a Quest * @dev Increases the reward per votes for a Quest * @param questID ID of the Quest * @param newRewardPerVote New amount of reward per veCRV (in wei) * @param addedRewardAmount Amount of rewards to add (in wei) * @param feeAmount Platform fees amount (in wei) */ function increaseQuestReward( uint256 questID, uint256 newRewardPerVote, uint256 addedRewardAmount, uint256 feeAmount ) external isAlive nonReentrant { if(questID >= nextID) revert Errors.InvalidQuestID(); if(msg.sender != quests[questID].creator) revert Errors.CallerNotAllowed(); if(newRewardPerVote == 0 || addedRewardAmount == 0 || feeAmount == 0) revert Errors.NullAmount(); uint256 remainingDuration = _getRemainingDuration(questID); //Also handles the Empty Quest check if(remainingDuration == 0) revert Errors.ExpiredQuest(); // The new reward amount must be higher uint256 nextPeriod = getCurrentPeriod() + WEEK; if(newRewardPerVote <= periodsByQuest[questID][nextPeriod].rewardPerVote) revert Errors.LowerRewardPerVote(); // For all non active QuestPeriods (non Closed, nor the current Active one) // Calculates the amount of reward token needed with the new rewardPerVote value // by calculating the new amount of reward per period, and the difference with the current amount of reward per period // to have the exact amount to add for each non-active period, and the exact total amount to add to the Quest // (because we don't want to pay for Periods that are Closed or the current period) uint256 newRewardPerPeriod = (periodsByQuest[questID][nextPeriod].objectiveVotes * newRewardPerVote) / UNIT; uint256 diffRewardPerPeriod = newRewardPerPeriod - periodsByQuest[questID][nextPeriod].rewardAmountPerPeriod; if((diffRewardPerPeriod * remainingDuration) != addedRewardAmount) revert Errors.IncorrectAddedRewardAmount(); if((addedRewardAmount * platformFee)/MAX_BPS != feeAmount) revert Errors.IncorrectFeeAmount(); address rewardToken = quests[questID].rewardToken; // Pull all the rewards in this contract IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), addedRewardAmount); // And transfer the fees from the Quest creator to the Chest contract IERC20(rewardToken).safeTransferFrom(msg.sender, questChest, feeAmount); uint256 periodIterator = nextPeriod; uint256 lastPeriod = questPeriods[questID][questPeriods[questID].length - 1]; // Update the Quest struct with the added reward amount quests[questID].totalRewardAmount += addedRewardAmount; // Update all QuestPeriods, starting with the nextPeriod one for(uint256 i; i < remainingDuration;){ if(periodIterator > lastPeriod) break; //Safety check, we never want to write on non-initialized QuestPeriods (that were not initialized) // And update each QuestPeriod with the new values periodsByQuest[questID][periodIterator].rewardPerVote = newRewardPerVote; periodsByQuest[questID][periodIterator].rewardAmountPerPeriod = newRewardPerPeriod; periodIterator = ((periodIterator + WEEK) / WEEK) * WEEK; unchecked{ ++i; } } emit IncreasedQuestReward(questID, nextPeriod, newRewardPerVote, addedRewardAmount); } /** * @notice Increases the target bias/veCRV amount to reach on the Gauge * @dev CIncreases the target bias/veCRV amount to reach on the Gauge * @param questID ID of the Quest * @param newObjective New target bias to reach (equivalent to amount of veCRV in wei to reach) * @param addedRewardAmount Amount of rewards to add (in wei) * @param feeAmount Platform fees amount (in wei) */ function increaseQuestObjective( uint256 questID, uint256 newObjective, uint256 addedRewardAmount, uint256 feeAmount ) external isAlive nonReentrant { if(questID >= nextID) revert Errors.InvalidQuestID(); if(msg.sender != quests[questID].creator) revert Errors.CallerNotAllowed(); if(addedRewardAmount == 0 || feeAmount == 0) revert Errors.NullAmount(); uint256 remainingDuration = _getRemainingDuration(questID); //Also handles the Empty Quest check if(remainingDuration == 0) revert Errors.ExpiredQuest(); // No need to compare to minObjective : the new value must be higher than current Objective // and current objective needs to be >= minObjective uint256 nextPeriod = getCurrentPeriod() + WEEK; if(newObjective <= periodsByQuest[questID][nextPeriod].objectiveVotes) revert Errors.LowerObjective(); // For all non active QuestPeriods (non Closed, nor the current Active one) // Calculates the amount of reward token needed with the new objective bias // by calculating the new amount of reward per period, and the difference with the current amount of reward per period // to have the exact amount to add for each non-active period, and the exact total amount to add to the Quest // (because we don't want to pay for Periods that are Closed or the current period) uint256 newRewardPerPeriod = (newObjective * periodsByQuest[questID][nextPeriod].rewardPerVote) / UNIT; uint256 diffRewardPerPeriod = newRewardPerPeriod - periodsByQuest[questID][nextPeriod].rewardAmountPerPeriod; if((diffRewardPerPeriod * remainingDuration) != addedRewardAmount) revert Errors.IncorrectAddedRewardAmount(); if((addedRewardAmount * platformFee)/MAX_BPS != feeAmount) revert Errors.IncorrectFeeAmount(); address rewardToken = quests[questID].rewardToken; // Pull all the rewards in this contract IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), addedRewardAmount); // And transfer the fees from the Quest creator to the Chest contract IERC20(rewardToken).safeTransferFrom(msg.sender, questChest, feeAmount); uint256 periodIterator = nextPeriod; uint256 lastPeriod = questPeriods[questID][questPeriods[questID].length - 1]; // Update the Quest struct with the added reward amount quests[questID].totalRewardAmount += addedRewardAmount; // Update all QuestPeriods, starting with the nextPeriod one for(uint256 i; i < remainingDuration;){ if(periodIterator > lastPeriod) break; //Safety check, we never want to write on non-existing QuestPeriods (that were not initialized) // And update each QuestPeriod with the new values periodsByQuest[questID][periodIterator].objectiveVotes = newObjective; periodsByQuest[questID][periodIterator].rewardAmountPerPeriod = newRewardPerPeriod; periodIterator = ((periodIterator + WEEK) / WEEK) * WEEK; unchecked{ ++i; } } emit IncreasedQuestObjective(questID, nextPeriod, newObjective, addedRewardAmount); } /** * @notice Withdraw all undistributed rewards from Closed Quest Periods * @dev Withdraw all undistributed rewards from Closed Quest Periods * @param questID ID of the Quest * @param recipient Address to send the reward tokens to */ function withdrawUnusedRewards(uint256 questID, address recipient) external isAlive nonReentrant { if(questID >= nextID) revert Errors.InvalidQuestID(); if(msg.sender != quests[questID].creator) revert Errors.CallerNotAllowed(); if(recipient == address(0)) revert Errors.ZeroAddress(); // Total amount available to withdraw uint256 totalWithdraw; uint48[] memory _questPeriods = questPeriods[questID]; uint256 length = _questPeriods.length; for(uint256 i; i < length;){ QuestPeriod storage _questPeriod = periodsByQuest[questID][_questPeriods[i]]; // We allow to withdraw unused rewards after the period was closed, or after it was distributed if(_questPeriod.currentState == PeriodState.ACTIVE) { unchecked{ ++i; } continue; } uint256 withdrawableForPeriod = _questPeriod.withdrawableAmount; // If there is token to withdraw for that period, add they to the total to withdraw, // and set the withdrawable amount to 0 if(withdrawableForPeriod != 0){ totalWithdraw += withdrawableForPeriod; _questPeriod.withdrawableAmount = 0; } unchecked{ ++i; } } // If there is a non null amount of token to withdraw, execute a transfer if(totalWithdraw != 0){ address rewardToken = quests[questID].rewardToken; IERC20(rewardToken).safeTransfer(recipient, totalWithdraw); emit WithdrawUnusedRewards(questID, recipient, totalWithdraw); } } /** * @notice Emergency withdraws all undistributed rewards from Closed Quest Periods & all rewards for Active Periods * @dev Emergency withdraws all undistributed rewards from Closed Quest Periods & all rewards for Active Periods * @param questID ID of the Quest * @param recipient Address to send the reward tokens to */ function emergencyWithdraw(uint256 questID, address recipient) external nonReentrant { if(!isKilled) revert Errors.NotKilled(); if(block.timestamp < kill_ts + KILL_DELAY) revert Errors.KillDelayNotExpired(); if(questID >= nextID) revert Errors.InvalidQuestID(); if(msg.sender != quests[questID].creator) revert Errors.CallerNotAllowed(); if(recipient == address(0)) revert Errors.ZeroAddress(); // Total amount to emergency withdraw uint256 totalWithdraw; uint48[] memory _questPeriods = questPeriods[questID]; uint256 length = _questPeriods.length; for(uint256 i; i < length;){ QuestPeriod storage _questPeriod = periodsByQuest[questID][_questPeriods[i]]; // For CLOSED or DISTRIBUTED periods if(_questPeriod.currentState != PeriodState.ACTIVE){ uint256 withdrawableForPeriod = _questPeriod.withdrawableAmount; // If there is a non_null withdrawable amount for the period, // add it to the total to withdraw, et set the withdrawable amount ot 0 if(withdrawableForPeriod != 0){ totalWithdraw += withdrawableForPeriod; _questPeriod.withdrawableAmount = 0; } } else { // And for the active period, and the next ones, withdraw the total reward amount totalWithdraw += _questPeriod.rewardAmountPerPeriod; _questPeriod.rewardAmountPerPeriod = 0; } unchecked{ ++i; } } // If the total amount to emergency withdraw is non_null, execute a transfer if(totalWithdraw != 0){ address rewardToken = quests[questID].rewardToken; IERC20(rewardToken).safeTransfer(recipient, totalWithdraw); emit EmergencyWithdraw(questID, recipient, totalWithdraw); } } // Manager functions function _getPeriodBias(address gauge, uint256 period) internal view returns(uint256 periodBias) { IGaugeController gaugeController = IGaugeController(GAUGE_CONTROLLER); uint256 lastUserVote = gaugeController.last_user_vote(GAUGE_VOTER, gauge); IGaugeController.VotedSlope memory voteUserSlope = gaugeController.vote_user_slopes(GAUGE_VOTER, gauge); if(lastUserVote > period) return 0; if(voteUserSlope.end <= period) return 0; if(voteUserSlope.slope == 0) return 0; periodBias = voteUserSlope.slope * (voteUserSlope.end - period); } function _closeQuestPeriod(uint256 period, uint256 questID) internal returns(bool) { // We check that this period was not already closed if(periodsByQuest[questID][period].currentState != PeriodState.ACTIVE) return false; // We use the Gauge Point data from nextPeriod => the end of the period we are closing uint256 nextPeriod = period + WEEK; IGaugeController gaugeController = IGaugeController(GAUGE_CONTROLLER); Quest memory _quest = quests[questID]; QuestPeriod memory _questPeriod = periodsByQuest[questID][period]; _questPeriod.currentState = PeriodState.CLOSED; // Call a checkpoint on the Gauge, in case it was not written yet gaugeController.checkpoint_gauge(_quest.gauge); // Get the bias of the Gauge for the end of the period uint256 periodBias = _getPeriodBias(_quest.gauge, nextPeriod); if(periodBias == 0) { //Because we don't want to divide by 0 // Here since the bias is 0, we consider 0% completion // => no rewards to be distributed // We do not change _questPeriod.rewardAmountDistributed since the default value is already 0 _questPeriod.withdrawableAmount = _questPeriod.rewardAmountPerPeriod; } else{ // For here, if the Gauge Bias is equal or greater than the objective, // set all the period reward to be distributed. // If the bias is less, we take that bias, and calculate the amount of rewards based // on the rewardPerVote & the Gauge bias uint256 toDistributeAmount = periodBias >= _questPeriod.objectiveVotes ? _questPeriod.rewardAmountPerPeriod : (periodBias * _questPeriod.rewardPerVote) / UNIT; _questPeriod.rewardAmountDistributed = toDistributeAmount; // And the rest is set as withdrawable amount, that the Quest creator can retrieve _questPeriod.withdrawableAmount = _questPeriod.rewardAmountPerPeriod - toDistributeAmount; address questDistributor = questDistributors[questID]; if(!MultiMerkleDistributor(questDistributor).addQuestPeriod(questID, period, toDistributeAmount)) revert Errors.DisitributorFail(); IERC20(_quest.rewardToken).safeTransfer(questDistributor, toDistributeAmount); } periodsByQuest[questID][period] = _questPeriod; emit PeriodClosed(questID, period); return true; } /** * @notice Closes the Period, and all QuestPeriods for this period * @dev Closes all QuestPeriod for the given period, calculating rewards to distribute & send them to distributor * @param period Timestamp of the period */ function closeQuestPeriod(uint256 period) external isAlive onlyAllowed nonReentrant returns(uint256 closed, uint256 skipped) { period = (period / WEEK) * WEEK; if(distributor == address(0)) revert Errors.NoDistributorSet(); if(period == 0) revert Errors.InvalidPeriod(); if(period >= getCurrentPeriod()) revert Errors.PeriodStillActive(); if(questsByPeriod[period].length == 0) revert Errors.EmptyPeriod(); // We use the 1st QuestPeriod of this period to check it was not Closed uint256[] memory questsForPeriod = questsByPeriod[period]; // For each QuestPeriod uint256 length = questsForPeriod.length; for(uint256 i = 0; i < length;){ bool result = _closeQuestPeriod(period, questsForPeriod[i]); if(result){ closed++; } else { skipped++; } unchecked{ ++i; } } } /** * @notice Closes the given QuestPeriods for the Period * @dev Closes the given QuestPeriods for the Period, calculating rewards to distribute & send them to distributor * @param period Timestamp of the period * @param questIDs List of the Quest IDs to close */ function closePartOfQuestPeriod(uint256 period, uint256[] calldata questIDs) external isAlive onlyAllowed nonReentrant returns(uint256 closed, uint256 skipped) { period = (period / WEEK) * WEEK; uint256 questIDLength = questIDs.length; if(questIDLength == 0) revert Errors.EmptyArray(); if(distributor == address(0)) revert Errors.NoDistributorSet(); if(period == 0) revert Errors.InvalidPeriod(); if(period >= getCurrentPeriod()) revert Errors.PeriodStillActive(); if(questsByPeriod[period].length == 0) revert Errors.EmptyPeriod(); // For each QuestPeriod for(uint256 i = 0; i < questIDLength;){ bool result = _closeQuestPeriod(period, questIDs[i]); if(result){ closed++; } else { skipped++; } unchecked{ ++i; } } } /** * @dev Sets the QuestPeriod as disitrbuted, and adds the MerkleRoot to the Distributor contract * @param questID ID of the Quest * @param period Timestamp of the period * @param totalAmount sum of all rewards for the Merkle Tree * @param merkleRoot MerkleRoot to add */ function _addMerkleRoot(uint256 questID, uint256 period, uint256 totalAmount, bytes32 merkleRoot) internal { if(questID >= nextID) revert Errors.InvalidQuestID(); if(merkleRoot == 0) revert Errors.EmptyMerkleRoot(); if(totalAmount == 0) revert Errors.NullAmount(); // This also allows to check if the given period is correct => If not, the currentState is never set to CLOSED for the QuestPeriod if(periodsByQuest[questID][period].currentState != PeriodState.CLOSED) revert Errors.PeriodNotClosed(); // Add the MerkleRoot to the Distributor & set the QuestPeriod as DISTRIBUTED if(!MultiMerkleDistributor(questDistributors[questID]).updateQuestPeriod(questID, period, totalAmount, merkleRoot)) revert Errors.DisitributorFail(); periodsByQuest[questID][period].currentState = PeriodState.DISTRIBUTED; } /** * @notice Sets the QuestPeriod as disitrbuted, and adds the MerkleRoot to the Distributor contract * @dev internal call to _addMerkleRoot() * @param questID ID of the Quest * @param period Timestamp of the period * @param totalAmount sum of all rewards for the Merkle Tree * @param merkleRoot MerkleRoot to add */ function addMerkleRoot(uint256 questID, uint256 period, uint256 totalAmount, bytes32 merkleRoot) external isAlive onlyAllowed nonReentrant { period = (period / WEEK) * WEEK; _addMerkleRoot(questID, period, totalAmount, merkleRoot); } /** * @notice Sets a list of QuestPeriods as disitrbuted, and adds the MerkleRoot to the Distributor contract for each * @dev Loop and internal call to _addMerkleRoot() * @param questIDs List of Quest IDs * @param period Timestamp of the period * @param totalAmounts List of sums of all rewards for the Merkle Tree * @param merkleRoots List of MerkleRoots to add */ function addMultipleMerkleRoot( uint256[] calldata questIDs, uint256 period, uint256[] calldata totalAmounts, bytes32[] calldata merkleRoots ) external isAlive onlyAllowed nonReentrant { period = (period / WEEK) * WEEK; uint256 length = questIDs.length; if(length != merkleRoots.length) revert Errors.InequalArraySizes(); if(length != totalAmounts.length) revert Errors.InequalArraySizes(); for(uint256 i = 0; i < length;){ _addMerkleRoot(questIDs[i], period, totalAmounts[i], merkleRoots[i]); unchecked{ ++i; } } } /** * @notice Whitelists a reward token * @dev Whitelists a reward token * @param newToken Address of the reward token */ function whitelistToken(address newToken, uint256 minRewardPerVote) public onlyAllowed { if(newToken == address(0)) revert Errors.ZeroAddress(); if(minRewardPerVote == 0) revert Errors.InvalidParameter(); whitelistedTokens[newToken] = true; minRewardPerVotePerToken[newToken] = minRewardPerVote; emit WhitelistToken(newToken, minRewardPerVote); } /** * @notice Whitelists a list of reward tokens * @dev Whitelists a list of reward tokens * @param newTokens List of reward tokens addresses */ function whitelistMultipleTokens(address[] calldata newTokens, uint256[] calldata minRewardPerVotes) external onlyAllowed { uint256 length = newTokens.length; if(length == 0) revert Errors.EmptyArray(); if(length != minRewardPerVotes.length) revert Errors.InequalArraySizes(); for(uint256 i = 0; i < length;){ whitelistToken(newTokens[i], minRewardPerVotes[i]); unchecked{ ++i; } } } function updateRewardToken(address newToken, uint256 newMinRewardPerVote) external onlyAllowed { if(!whitelistedTokens[newToken]) revert Errors.TokenNotWhitelisted(); if(newMinRewardPerVote == 0) revert Errors.InvalidParameter(); minRewardPerVotePerToken[newToken] = newMinRewardPerVote; emit UpdateRewardToken(newToken, newMinRewardPerVote); } // Admin functions /** * @notice Sets an initial Distributor address * @dev Sets an initial Distributor address * @param newDistributor Address of the Distributor */ function initiateDistributor(address newDistributor) external onlyOwner { if(distributor != address(0)) revert Errors.AlreadyInitialized(); distributor = newDistributor; emit InitDistributor(newDistributor); } /** * @notice Approves a new address as manager * @dev Approves a new address as manager * @param newManager Address to add */ function approveManager(address newManager) external onlyOwner { if(newManager == address(0)) revert Errors.ZeroAddress(); approvedManagers[newManager] = true; emit ApprovedManager(newManager); } /** * @notice Removes an address from the managers * @dev Removes an address from the managers * @param manager Address to remove */ function removeManager(address manager) external onlyOwner { if(manager == address(0)) revert Errors.ZeroAddress(); approvedManagers[manager] = false; emit RemovedManager(manager); } /** * @notice Updates the Chest address * @dev Updates the Chest address * @param chest Address of the new Chest */ function updateChest(address chest) external onlyOwner { if(chest == address(0)) revert Errors.ZeroAddress(); address oldChest = questChest; questChest = chest; emit ChestUpdated(oldChest, chest); } /** * @notice Updates the Distributor address * @dev Updates the Distributor address * @param newDistributor Address of the new Distributor */ function updateDistributor(address newDistributor) external onlyOwner { if(newDistributor == address(0)) revert Errors.ZeroAddress(); address oldDistributor = distributor; distributor = newDistributor; emit DistributorUpdated(oldDistributor, distributor); } /** * @notice Updates the Platfrom fees BPS ratio * @dev Updates the Platfrom fees BPS ratio * @param newFee New fee ratio */ function updatePlatformFee(uint256 newFee) external onlyOwner { if(newFee > 500) revert Errors.InvalidParameter(); uint256 oldfee = platformFee; platformFee = newFee; emit PlatformFeeUpdated(oldfee, newFee); } /** * @notice Updates the min objective value * @dev Updates the min objective value * @param newMinObjective New min objective */ function updateMinObjective(uint256 newMinObjective) external onlyOwner { if(newMinObjective == 0) revert Errors.InvalidParameter(); uint256 oldMinObjective = minObjective; minObjective = newMinObjective; emit MinObjectiveUpdated(oldMinObjective, newMinObjective); } /** * @notice Recovers ERC2O tokens sent by mistake to the contract * @dev Recovers ERC2O tokens sent by mistake to the contract * @param token Address tof the EC2O token * @return bool: success */ function recoverERC20(address token) external onlyOwner returns(bool) { if(whitelistedTokens[token]) revert Errors.CannotRecoverToken(); uint256 amount = IERC20(token).balanceOf(address(this)); if(amount == 0) revert Errors.NullAmount(); IERC20(token).safeTransfer(owner(), amount); return true; } /** * @notice Kills the contract * @dev Kills the contract */ function killBoard() external onlyOwner { if(isKilled) revert Errors.AlreadyKilled(); isKilled = true; kill_ts = block.timestamp; emit Killed(kill_ts); } /** * @notice Unkills the contract * @dev Unkills the contract */ function unkillBoard() external onlyOwner { if(!isKilled) revert Errors.NotKilled(); if(block.timestamp >= kill_ts + KILL_DELAY) revert Errors.KillDelayExpired(); isKilled = false; emit Unkilled(block.timestamp); } // Utils function safe48(uint n) internal pure returns (uint48) { if(n > type(uint48).max) revert Errors.NumberExceed48Bits(); return uint48(n); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../interfaces/IERC20.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; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.10; import "../oz/utils/Ownable.sol"; /** @title Extend OZ Ownable contract */ /// @author Paladin contract Owner is Ownable { address public pendingOwner; event NewPendingOwner(address indexed previousPendingOwner, address indexed newPendingOwner); error CannotBeOwner(); error CallerNotPendingOwner(); error ZeroAddress(); function transferOwnership(address newOwner) public override virtual onlyOwner { if(newOwner == address(0)) revert ZeroAddress(); if(newOwner == owner()) revert CannotBeOwner(); address oldPendingOwner = pendingOwner; pendingOwner = newOwner; emit NewPendingOwner(oldPendingOwner, newOwner); } function acceptOwnership() public virtual { if(msg.sender != pendingOwner) revert CallerNotPendingOwner(); address newOwner = pendingOwner; _transferOwnership(pendingOwner); pendingOwner = address(0); emit NewPendingOwner(newOwner, address(0)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (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() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
//██████╗ █████╗ ██╗ █████╗ ██████╗ ██╗███╗ ██╗ //██╔══██╗██╔══██╗██║ ██╔══██╗██╔══██╗██║████╗ ██║ //██████╔╝███████║██║ ███████║██║ ██║██║██╔██╗ ██║ //██╔═══╝ ██╔══██║██║ ██╔══██║██║ ██║██║██║╚██╗██║ //██║ ██║ ██║███████╗██║ ██║██████╔╝██║██║ ╚████║ //╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝ // SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "./oz/interfaces/IERC20.sol"; import "./oz/libraries/SafeERC20.sol"; import "./oz/utils/MerkleProof.sol"; import "./utils/Owner.sol"; import "./oz/utils/ReentrancyGuard.sol"; import "./utils/Errors.sol"; /** @title Warden Quest Multi Merkle Distributor */ /// @author Paladin /* Contract holds ERC20 rewards from Quests Can handle multiple MerkleRoots */ contract MultiMerkleDistributor is Owner, ReentrancyGuard { using SafeERC20 for IERC20; /** @notice Seconds in a Week */ uint256 private constant WEEK = 604800; /** @notice Mapping listing the reward token associated to each Quest ID */ // QuestID => reward token mapping(uint256 => address) public questRewardToken; /** @notice Mapping of tokens this contract is or was distributing */ // token address => boolean mapping(address => bool) public rewardTokens; //Periods: timestamp => start of a week, used as a voting period //in the Curve GaugeController though the timestamp / WEEK * WEEK logic. //Handled through the QuestManager contract. //Those can be fetched through this contract when they are closed, or through the QuestManager contract. /** @notice List of Closed QuestPeriods by Quest ID */ // QuestID => array of periods mapping(uint256 => uint256[]) public questClosedPeriods; /** @notice Merkle Root for each period of a Quest (indexed by Quest ID) */ // QuestID => period => merkleRoot mapping(uint256 => mapping(uint256 => bytes32)) public questMerkleRootPerPeriod; /** @notice Amount of rewards for each period of a Quest (indexed by Quest ID) */ // QuestID => period => totalRewardsAmount mapping(uint256 => mapping(uint256 => uint256)) public questRewardsPerPeriod; /** @notice BitMap of claims for each period of a Quest */ // QuestID => period => claimedBitMap // This is a packed array of booleans. mapping(uint256 => mapping(uint256 => mapping(uint256 => uint256))) private questPeriodClaimedBitMap; /** @notice Address of the QuestBoard contract */ address public immutable questBoard; // Events /** @notice Event emitted when an user Claims */ event Claimed( uint256 indexed questID, uint256 indexed period, uint256 index, uint256 amount, address rewardToken, address indexed account ); /** @notice Event emitted when a New Quest is added */ event NewQuest(uint256 indexed questID, address rewardToken); /** @notice Event emitted when a Period of a Quest is updated (when the Merkle Root is added) */ event QuestPeriodUpdated(uint256 indexed questID, uint256 indexed period, bytes32 merkleRoot); // Modifier /** @notice Check the caller is either the admin or the QuestBoard contract */ modifier onlyAllowed(){ if(msg.sender != questBoard && msg.sender != owner()) revert Errors.CallerNotAllowed(); _; } // Constructor constructor(address _questBoard){ if(_questBoard == address(0)) revert Errors.ZeroAddress(); questBoard = _questBoard; } // Functions /** * @notice Checks if the rewards were claimed for an user on a given period * @dev Checks if the rewards were claimed for an user (based on the index) on a given period * @param questID ID of the Quest * @param period Amount of underlying to borrow * @param index Index of the claim * @return bool : true if already claimed */ function isClaimed(uint256 questID, uint256 period, uint256 index) public view returns (bool) { uint256 claimedWordIndex = index >> 8; uint256 claimedBitIndex = index & 0xff; uint256 claimedWord = questPeriodClaimedBitMap[questID][period][claimedWordIndex]; uint256 mask = (1 << claimedBitIndex); return claimedWord & mask != 0; } /** * @dev Sets the rewards as claimed for the index on the given period * @param questID ID of the Quest * @param period Timestamp of the period * @param index Index of the claim */ function _setClaimed(uint256 questID, uint256 period, uint256 index) private { uint256 claimedWordIndex = index >> 8; uint256 claimedBitIndex = index & 0xff; questPeriodClaimedBitMap[questID][period][claimedWordIndex] |= (1 << claimedBitIndex); } //Basic Claim /** * @notice Claims the reward for an user for a given period of a Quest * @dev Claims the reward for an user for a given period of a Quest if the correct proof was given * @param questID ID of the Quest * @param period Timestamp of the period * @param index Index in the Merkle Tree * @param account Address of the user claiming the rewards * @param amount Amount of rewards to claim * @param merkleProof Proof to claim the rewards */ function claim(uint256 questID, uint256 period, uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof) public nonReentrant { if(account == address(0)) revert Errors.ZeroAddress(); if(questMerkleRootPerPeriod[questID][period] == 0) revert Errors.MerkleRootNotUpdated(); if(isClaimed(questID, period, index)) revert Errors.AlreadyClaimed(); // Check that the given parameters match the given Proof bytes32 node = keccak256(abi.encodePacked(questID, period, index, account, amount)); if(!MerkleProof.verify(merkleProof, questMerkleRootPerPeriod[questID][period], node)) revert Errors.InvalidProof(); // Set the rewards as claimed for that period // And transfer the rewards to the user address rewardToken = questRewardToken[questID]; _setClaimed(questID, period, index); questRewardsPerPeriod[questID][period] -= amount; IERC20(rewardToken).safeTransfer(account, amount); emit Claimed(questID, period, index, amount, rewardToken, account); } //Struct ClaimParams struct ClaimParams { uint256 questID; uint256 period; uint256 index; uint256 amount; bytes32[] merkleProof; } //Multi Claim /** * @notice Claims multiple rewards for a given list * @dev Calls the claim() method for each entry in the claims array * @param account Address of the user claiming the rewards * @param claims List of ClaimParams struct data to claim */ function multiClaim(address account, ClaimParams[] calldata claims) external { uint256 length = claims.length; if(length == 0) revert Errors.EmptyParameters(); for(uint256 i; i < length;){ claim(claims[i].questID, claims[i].period, claims[i].index, account, claims[i].amount, claims[i].merkleProof); unchecked{ ++i; } } } //FullQuest Claim (form of Multi Claim but for only one Quest => only one ERC20 transfer) //Only works for the given periods (in ClaimParams) for the Quest. Any omitted period will be skipped /** * @notice Claims the reward for all the given periods of a Quest, and transfer all the rewards at once * @dev Sums up all the rewards for given periods of a Quest, and executes only one transfer * @param account Address of the user claiming the rewards * @param questID ID of the Quest * @param claims List of ClaimParams struct data to claim */ function claimQuest(address account, uint256 questID, ClaimParams[] calldata claims) external nonReentrant { if(account == address(0)) revert Errors.ZeroAddress(); uint256 length = claims.length; if(length == 0) revert Errors.EmptyParameters(); // Total amount claimable, to transfer at once uint256 totalClaimAmount; address rewardToken = questRewardToken[questID]; for(uint256 i; i < length;){ if(claims[i].questID != questID) revert Errors.IncorrectQuestID(); if(questMerkleRootPerPeriod[questID][claims[i].period] == 0) revert Errors.MerkleRootNotUpdated(); if(isClaimed(questID, claims[i].period, claims[i].index)) revert Errors.AlreadyClaimed(); // For each period given, if the proof matches the given parameters, // set as claimed and add to the to total to transfer bytes32 node = keccak256(abi.encodePacked(questID, claims[i].period, claims[i].index, account, claims[i].amount)); if(!MerkleProof.verify(claims[i].merkleProof, questMerkleRootPerPeriod[questID][claims[i].period], node)) revert Errors.InvalidProof(); _setClaimed(questID, claims[i].period, claims[i].index); questRewardsPerPeriod[questID][claims[i].period] -= claims[i].amount; totalClaimAmount += claims[i].amount; emit Claimed(questID, claims[i].period, claims[i].index, claims[i].amount, rewardToken, account); unchecked{ ++i; } } // Transfer the total claimed amount IERC20(rewardToken).safeTransfer(account, totalClaimAmount); } /** * @notice Returns all current Closed periods for the given Quest ID * @dev Returns all current Closed periods for the given Quest ID * @param questID ID of the Quest * @return uint256[] : List of closed periods */ function getClosedPeriodsByQuests(uint256 questID) external view returns (uint256[] memory) { return questClosedPeriods[questID]; } // Manager functions /** * @notice Adds a new Quest to the listing * @dev Adds a new Quest ID and the associated reward token * @param questID ID of the Quest * @param token Address of the ERC20 reward token * @return bool : success */ function addQuest(uint256 questID, address token) external returns(bool) { if(msg.sender != questBoard) revert Errors.CallerNotAllowed(); if(questRewardToken[questID] != address(0)) revert Errors.QuestAlreadyListed(); if(token == address(0)) revert Errors.TokenNotWhitelisted(); // Add a new Quest using the QuestID, and list the reward token for that Quest questRewardToken[questID] = token; if(!rewardTokens[token]) rewardTokens[token] = true; emit NewQuest(questID, token); return true; } /** * @notice Adds a new period & the rewards of this period for a Quest * @dev Adds a new period & the rewards of this period for a Quest * @param questID ID of the Quest * @param period Timestamp of the period * @param totalRewardAmount Total amount of rewards to distribute for the period * @return bool : success */ function addQuestPeriod(uint256 questID, uint256 period, uint256 totalRewardAmount) external returns(bool) { period = (period / WEEK) * WEEK; if(msg.sender != questBoard) revert Errors.CallerNotAllowed(); if(questRewardToken[questID] == address(0)) revert Errors.QuestNotListed(); if(questRewardsPerPeriod[questID][period] != 0) revert Errors.PeriodAlreadyUpdated(); if(period == 0) revert Errors.IncorrectPeriod(); if(totalRewardAmount == 0) revert Errors.NullAmount(); questRewardsPerPeriod[questID][period] = totalRewardAmount; return true; } function fixQuestPeriod(uint256 questID, uint256 period, uint256 newTotalRewardAmount) external returns(bool) { if(msg.sender != questBoard) revert Errors.CallerNotAllowed(); period = (period / WEEK) * WEEK; if(questRewardToken[questID] == address(0)) revert Errors.QuestNotListed(); if(period == 0) revert Errors.IncorrectPeriod(); if(questRewardsPerPeriod[questID][period] == 0) revert Errors.PeriodNotListed(); uint256 previousTotalRewardAmount = questRewardsPerPeriod[questID][period]; questRewardsPerPeriod[questID][period] = newTotalRewardAmount; if(previousTotalRewardAmount > newTotalRewardAmount){ // Send back the extra amount of reward token that was incorrectly sent // In the case of missing reward token, the Board will send them to this contract uint256 extraAmount = previousTotalRewardAmount - newTotalRewardAmount; IERC20(questRewardToken[questID]).safeTransfer(questBoard, extraAmount); } return true; } /** * @notice Updates the period of a Quest by adding the Merkle Root * @dev Add the Merkle Root for the eriod of the given Quest * @param questID ID of the Quest * @param period timestamp of the period * @param totalAmount sum of all rewards for the Merkle Tree * @param merkleRoot MerkleRoot to add * @return bool: success */ function updateQuestPeriod(uint256 questID, uint256 period, uint256 totalAmount, bytes32 merkleRoot) external onlyAllowed returns(bool) { period = (period / WEEK) * WEEK; if(questRewardToken[questID] == address(0)) revert Errors.QuestNotListed(); if(period == 0) revert Errors.IncorrectPeriod(); if(questRewardsPerPeriod[questID][period] == 0) revert Errors.PeriodNotListed(); if(questMerkleRootPerPeriod[questID][period] != 0) revert Errors.PeriodAlreadyUpdated(); if(merkleRoot == 0) revert Errors.EmptyMerkleRoot(); // Add a new Closed Period for the Quest questClosedPeriods[questID].push(period); if(totalAmount != questRewardsPerPeriod[questID][period]) revert Errors.IncorrectRewardAmount(); // Add the new MerkleRoot for that Closed Period questMerkleRootPerPeriod[questID][period] = merkleRoot; emit QuestPeriodUpdated(questID, period, merkleRoot); return true; } // Admin functions /** * @notice Recovers ERC2O tokens sent by mistake to the contract * @dev Recovers ERC2O tokens sent by mistake to the contract * @param token Address tof the EC2O token * @return bool: success */ function recoverERC20(address token) external onlyOwner nonReentrant returns(bool) { if(rewardTokens[token]) revert Errors.CannotRecoverToken(); uint256 amount = IERC20(token).balanceOf(address(this)); if(amount == 0) revert Errors.NullAmount(); IERC20(token).safeTransfer(owner(), amount); return true; } // /** * @notice Allows to update the MerkleRoot for a given period of a Quest if the current Root is incorrect * @dev Updates the MerkleRoot for the period of the Quest * @param questID ID of the Quest * @param period Timestamp of the period * @param merkleRoot New MerkleRoot to add * @return bool : success */ function emergencyUpdateQuestPeriod(uint256 questID, uint256 period, uint256 addedRewardAmount, bytes32 merkleRoot) external onlyOwner returns(bool) { // In case the given MerkleRoot was incorrect: // Process: // 1 - block claims for the Quest period by using this method to set an incorrect MerkleRoot, where no proof matches the root // 2 - prepare a new Merkle Tree, taking in account user previous claims on that period, and missing/overpaid rewards // a - for all new claims to be added, set them after the last index of the previous Merkle Tree // b - for users that did not claim, keep the same index, and adjust the amount to claim if needed // c - for indexes that were claimed, place an empty node in the Merkle Tree (with an amount at 0 & the address 0xdead as the account) // 3 - update the Quest period with the correct MerkleRoot // (no need to change the Bitmap, as the new MerkleTree will account for the indexes already claimed) period = (period / WEEK) * WEEK; if(questRewardToken[questID] == address(0)) revert Errors.QuestNotListed(); if(period == 0) revert Errors.IncorrectPeriod(); if(questMerkleRootPerPeriod[questID][period] == 0) revert Errors.PeriodNotClosed(); if(merkleRoot == 0) revert Errors.EmptyMerkleRoot(); questMerkleRootPerPeriod[questID][period] = merkleRoot; questRewardsPerPeriod[questID][period] += addedRewardAmount; emit QuestPeriodUpdated(questID, period, merkleRoot); return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Interface made for the Curve's GaugeController contract */ interface IGaugeController { struct VotedSlope { uint slope; uint power; uint end; } struct Point { uint bias; uint slope; } function vote_user_slopes(address, address) external view returns(VotedSlope memory); function last_user_vote(address, address) external view returns(uint); function points_weight(address, uint256) external view returns(Point memory); function checkpoint_gauge(address) external; function gauge_types(address _addr) external view returns(int128); }
pragma solidity 0.8.10; //SPDX-License-Identifier: MIT library Errors { // Common Errors error ZeroAddress(); error NullAmount(); error CallerNotAllowed(); error IncorrectRewardToken(); error SameAddress(); error InequalArraySizes(); error EmptyArray(); error EmptyParameters(); error AlreadyInitialized(); error InvalidParameter(); error CannotRecoverToken(); error ForbiddenCall(); error Killed(); error AlreadyKilled(); error NotKilled(); error KillDelayExpired(); error KillDelayNotExpired(); // Merkle Errors error MerkleRootNotUpdated(); error AlreadyClaimed(); error InvalidProof(); error EmptyMerkleRoot(); error IncorrectRewardAmount(); error MerkleRootFrozen(); error NotFrozen(); error AlreadyFrozen(); // Quest Errors error CallerNotQuestBoard(); error IncorrectQuestID(); error IncorrectPeriod(); error TokenNotWhitelisted(); error QuestAlreadyListed(); error QuestNotListed(); error PeriodAlreadyUpdated(); error PeriodNotClosed(); error PeriodStillActive(); error PeriodNotListed(); error EmptyQuest(); error EmptyPeriod(); error ExpiredQuest(); error NoDistributorSet(); error DisitributorFail(); error InvalidGauge(); error InvalidQuestID(); error InvalidPeriod(); error ObjectiveTooLow(); error RewardPerVoteTooLow(); error IncorrectDuration(); error IncorrectAddDuration(); error IncorrectTotalRewardAmount(); error IncorrectAddedRewardAmount(); error IncorrectFeeAmount(); error CalletNotQuestCreator(); error LowerRewardPerVote(); error LowerObjective(); error AlreadyBlacklisted(); error CreatorNotAllowed(); error AlreadyListed(); error NotListed(); //Math error NumberExceed48Bits(); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (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 * ==== * * [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason 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 { // 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 assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "./Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// 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/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_gaugeController","type":"address"},{"internalType":"address","name":"_gaugeVoter","type":"address"},{"internalType":"address","name":"_chest","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"AlreadyKilled","type":"error"},{"inputs":[],"name":"CallerNotAllowed","type":"error"},{"inputs":[],"name":"CallerNotPendingOwner","type":"error"},{"inputs":[],"name":"CannotBeOwner","type":"error"},{"inputs":[],"name":"CannotRecoverToken","type":"error"},{"inputs":[],"name":"DisitributorFail","type":"error"},{"inputs":[],"name":"EmptyArray","type":"error"},{"inputs":[],"name":"EmptyMerkleRoot","type":"error"},{"inputs":[],"name":"EmptyPeriod","type":"error"},{"inputs":[],"name":"EmptyQuest","type":"error"},{"inputs":[],"name":"ExpiredQuest","type":"error"},{"inputs":[],"name":"IncorrectAddDuration","type":"error"},{"inputs":[],"name":"IncorrectAddedRewardAmount","type":"error"},{"inputs":[],"name":"IncorrectDuration","type":"error"},{"inputs":[],"name":"IncorrectFeeAmount","type":"error"},{"inputs":[],"name":"IncorrectTotalRewardAmount","type":"error"},{"inputs":[],"name":"InequalArraySizes","type":"error"},{"inputs":[],"name":"InvalidGauge","type":"error"},{"inputs":[],"name":"InvalidParameter","type":"error"},{"inputs":[],"name":"InvalidPeriod","type":"error"},{"inputs":[],"name":"InvalidQuestID","type":"error"},{"inputs":[],"name":"KillDelayExpired","type":"error"},{"inputs":[],"name":"KillDelayNotExpired","type":"error"},{"inputs":[],"name":"Killed","type":"error"},{"inputs":[],"name":"LowerObjective","type":"error"},{"inputs":[],"name":"LowerRewardPerVote","type":"error"},{"inputs":[],"name":"NoDistributorSet","type":"error"},{"inputs":[],"name":"NotKilled","type":"error"},{"inputs":[],"name":"NullAmount","type":"error"},{"inputs":[],"name":"NumberExceed48Bits","type":"error"},{"inputs":[],"name":"ObjectiveTooLow","type":"error"},{"inputs":[],"name":"PeriodNotClosed","type":"error"},{"inputs":[],"name":"PeriodStillActive","type":"error"},{"inputs":[],"name":"RewardPerVoteTooLow","type":"error"},{"inputs":[],"name":"SameAddress","type":"error"},{"inputs":[],"name":"TokenNotWhitelisted","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"ApprovedManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldChest","type":"address"},{"indexed":false,"internalType":"address","name":"newChest","type":"address"}],"name":"ChestUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldDistributor","type":"address"},{"indexed":false,"internalType":"address","name":"newDistributor","type":"address"}],"name":"DistributorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedRewardAmount","type":"uint256"}],"name":"IncreasedQuestDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"updatePeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newObjective","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedRewardAmount","type":"uint256"}],"name":"IncreasedQuestObjective","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"updatePeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newRewardPerVote","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"addedRewardAmount","type":"uint256"}],"name":"IncreasedQuestReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"distributor","type":"address"}],"name":"InitDistributor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"killTime","type":"uint256"}],"name":"Killed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMinObjective","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMinObjective","type":"uint256"}],"name":"MinObjectiveUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousPendingOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"NewPendingOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint48","name":"duration","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"startPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"objectiveVotes","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardPerVote","type":"uint256"}],"name":"NewQuest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"period","type":"uint256"}],"name":"PeriodClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldfee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"PlatformFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"RemovedManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"unkillTime","type":"uint256"}],"name":"Unkilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newMinRewardPerVote","type":"uint256"}],"name":"UpdateRewardToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"minRewardPerVote","type":"uint256"}],"name":"WhitelistToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"questID","type":"uint256"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawUnusedRewards","type":"event"},{"inputs":[],"name":"GAUGE_CONTROLLER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAUGE_VOTER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KILL_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256","name":"totalAmount","type":"uint256"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"name":"addMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"questIDs","type":"uint256[]"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256[]","name":"totalAmounts","type":"uint256[]"},{"internalType":"bytes32[]","name":"merkleRoots","type":"bytes32[]"}],"name":"addMultipleMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newManager","type":"address"}],"name":"approveManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256[]","name":"questIDs","type":"uint256[]"}],"name":"closePartOfQuestPeriod","outputs":[{"internalType":"uint256","name":"closed","type":"uint256"},{"internalType":"uint256","name":"skipped","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"closeQuestPeriod","outputs":[{"internalType":"uint256","name":"closed","type":"uint256"},{"internalType":"uint256","name":"skipped","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint48","name":"duration","type":"uint48"},{"internalType":"uint256","name":"objective","type":"uint256"},{"internalType":"uint256","name":"rewardPerVote","type":"uint256"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"createQuest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"questId","type":"uint256"}],"name":"getAllPeriodsForQuestId","outputs":[{"internalType":"uint48[]","name":"","type":"uint48[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"questId","type":"uint256"}],"name":"getAllQuestPeriodsForQuestId","outputs":[{"components":[{"internalType":"uint256","name":"rewardAmountPerPeriod","type":"uint256"},{"internalType":"uint256","name":"rewardPerVote","type":"uint256"},{"internalType":"uint256","name":"objectiveVotes","type":"uint256"},{"internalType":"uint256","name":"rewardAmountDistributed","type":"uint256"},{"internalType":"uint256","name":"withdrawableAmount","type":"uint256"},{"internalType":"uint48","name":"periodStart","type":"uint48"},{"internalType":"enum LightQuestBoard.PeriodState","name":"currentState","type":"uint8"}],"internalType":"struct LightQuestBoard.QuestPeriod[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"period","type":"uint256"}],"name":"getQuestIdsForPeriod","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"uint48","name":"addedDuration","type":"uint48"},{"internalType":"uint256","name":"addedRewardAmount","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"increaseQuestDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"uint256","name":"newObjective","type":"uint256"},{"internalType":"uint256","name":"addedRewardAmount","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"increaseQuestObjective","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"uint256","name":"newRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"addedRewardAmount","type":"uint256"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"increaseQuestReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDistributor","type":"address"}],"name":"initiateDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isKilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"killBoard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"kill_ts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minObjective","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"minRewardPerVotePerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"periodsByQuest","outputs":[{"internalType":"uint256","name":"rewardAmountPerPeriod","type":"uint256"},{"internalType":"uint256","name":"rewardPerVote","type":"uint256"},{"internalType":"uint256","name":"objectiveVotes","type":"uint256"},{"internalType":"uint256","name":"rewardAmountDistributed","type":"uint256"},{"internalType":"uint256","name":"withdrawableAmount","type":"uint256"},{"internalType":"uint48","name":"periodStart","type":"uint48"},{"internalType":"enum LightQuestBoard.PeriodState","name":"currentState","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"questChest","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"questDistributors","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"questPeriods","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"quests","outputs":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint48","name":"duration","type":"uint48"},{"internalType":"uint48","name":"periodStart","type":"uint48"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"questsByPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"recoverERC20","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"}],"name":"removeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unkillBoard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"chest","type":"address"}],"name":"updateChest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDistributor","type":"address"}],"name":"updateDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinObjective","type":"uint256"}],"name":"updateMinObjective","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"updatePlatformFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newToken","type":"address"},{"internalType":"uint256","name":"newMinRewardPerVote","type":"uint256"}],"name":"updateRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"newTokens","type":"address[]"},{"internalType":"uint256[]","name":"minRewardPerVotes","type":"uint256[]"}],"name":"whitelistMultipleTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newToken","type":"address"},{"internalType":"uint256","name":"minRewardPerVote","type":"uint256"}],"name":"whitelistToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawUnusedRewards","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c06040526101906009553480156200001757600080fd5b5060405162004d4638038062004d468339810160408190526200003a9162000189565b62000045336200011c565b60016002556001600160a01b038316620000725760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166200009a5760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b0316836001600160a01b03161415620000ce5760405163367558c360e01b815260040160405180910390fd5b6001600160a01b0383811660805282811660a052600b80546001600160a01b0319169183169190911790556200010f670de0b6b3a76400006103e8620001d3565b600a555062000201915050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200018457600080fd5b919050565b6000806000606084860312156200019f57600080fd5b620001aa846200016c565b9250620001ba602085016200016c565b9150620001ca604085016200016c565b90509250925092565b6000816000190483118215151615620001fc57634e487b7160e01b600052601160045260246000fd5b500290565b60805160a051614afc6200024a6000396000818161030801528181613ef50152613faf0152600081816103630152818161212e015281816138c60152613f280152614afc6000f3fe608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063a78d0fbd11610182578063d52f1135116100e9578063eca5d0ca116100a2578063f2fde38b1161007c578063f2fde38b14610786578063f634f8d514610799578063f83d658b146107a2578063f9981140146107b557600080fd5b8063eca5d0ca14610740578063f29e9aa714610753578063f2ed9db61461077357600080fd5b8063d52f113514610634578063daf9c21014610647578063dc34a1511461066a578063e085f98014610672578063e30c39781461071a578063e928ef271461072d57600080fd5b8063ba82c8971161013b578063ba82c897146105d6578063bc30a618146105e9578063bfe10928146105fc578063c4e16b7d1461060f578063c6c02cac14610622578063ce7946b61461062a57600080fd5b8063a78d0fbd1461050c578063aa0b598814610515578063ac18de4314610528578063b3190eb91461053b578063b5eee7221461054e578063ba28facd1461056157600080fd5b806345c46147116102265780637f0301b2116101df5780637f0301b2146104925780638da5cb5b146104a55780638f4c72d8146104b65780638fe8a101146104c95780639e8c708e146104e6578063a6c93d7d146104f957600080fd5b806345c46147146103f05780636f42e67f1461041a578063715018a61461043a5780637211a52b1461044257806379ba50971461046a57806379dc77d01461047257600080fd5b806322884e2c1161027857806322884e2c1461035e578063255e7d1a1461038557806326232a2e146103985780632f940c70146103a15780633d723877146103b45780633da8a8d1146103c757600080fd5b8063086146d2146102c05780630881b830146102db57806311195485146102f057806319d227e81461030357806319e0fe1f146103425780631e96917d14610355575b600080fd5b6102c86107d5565b6040519081526020015b60405180910390f35b6102ee6102e936600461437a565b6107f4565b005b6102ee6102fe3660046143e6565b6108df565b61032a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016102d2565b6102ee61035036600461448a565b610a4d565b6102c860035481565b61032a7f000000000000000000000000000000000000000000000000000000000000000081565b6102ee6103933660046144d2565b610b11565b6102c860095481565b6102ee6103af366004614524565b610fae565b6102ee6103c2366004614550565b61127e565b61032a6103d5366004614550565b6008602052600090815260409020546001600160a01b031681565b6104036103fe366004614569565b61130c565b60405165ffffffffffff90911681526020016102d2565b61042d610428366004614550565b611357565b6040516102d291906145c3565b6102ee6114db565b610455610450366004614550565b611511565b604080519283526020830191909152016102d2565b6102ee61172f565b6102c8610480366004614659565b600f6020526000908152604090205481565b6102ee6104a036600461448a565b6117b8565b6000546001600160a01b031661032a565b6102ee6104c4366004614524565b611b65565b6010546104d69060ff1681565b60405190151581526020016102d2565b6104d66104f4366004614659565b611de5565b600b5461032a906001600160a01b031681565b6102c860115481565b6102ee610523366004614550565b611f06565b6102ee610536366004614659565b611f91565b6102c8610549366004614674565b61202b565b6102ee61055c366004614659565b612861565b6105c361056f366004614569565b6006602090815260009283526040808420909152908252902080546001820154600283015460038401546004850154600590950154939492939192909165ffffffffffff811690600160301b900460ff1687565b6040516102d297969594939291906146dc565b6102ee6105e4366004614726565b6128fe565b6102ee6105f7366004614659565b6129f7565b600c5461032a906001600160a01b031681565b6102ee61061d366004614726565b612aa2565b6102ee612b8e565b6102c86212750081565b610455610642366004614750565b612c4b565b6104d6610655366004614659565b600e6020526000908152604090205460ff1681565b6102ee612e27565b6106d1610680366004614550565b60046020526000908152604090208054600182015460028301546003909301546001600160a01b03928316939183169282169165ffffffffffff600160a01b8204811692600160d01b909204169086565b604080516001600160a01b0397881681529587166020870152939095169284019290925265ffffffffffff908116606084015216608082015260a081019190915260c0016102d2565b60015461032a906001600160a01b031681565b6102c861073b366004614569565b612eba565b6102ee61074e366004614659565b612eeb565b610766610761366004614550565b612f96565b6040516102d2919061479c565b6102ee610781366004614659565b613028565b6102ee610794366004614659565b6130cf565b6102c8600a5481565b6102ee6107b036600461448a565b6131a1565b6107c86107c3366004614550565b61352e565b6040516102d291906147e8565b600062093a806107e58142614836565b6107ef9190614858565b905090565b336000908152600d602052604090205460ff1615801561081f57506000546001600160a01b03163314155b1561083d5760405163015783e960e51b815260040160405180910390fd5b828061085c5760405163521299a960e01b815260040160405180910390fd5b80821461087c5760405163c155fe2160e01b815260040160405180910390fd5b60005b818110156108d7576108cf86868381811061089c5761089c614877565b90506020020160208101906108b19190614659565b8585848181106108c3576108c3614877565b90506020020135612aa2565b60010161087f565b505050505050565b60105460ff161561090357604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff1615801561092e57506000546001600160a01b03163314155b1561094c5760405163015783e960e51b815260040160405180910390fd5b6002805414156109775760405162461bcd60e51b815260040161096e9061488d565b60405180910390fd5b6002805562093a806109898187614836565b6109939190614858565b9450858181146109b65760405163c155fe2160e01b815260040160405180910390fd5b8084146109d65760405163c155fe2160e01b815260040160405180910390fd5b60005b81811015610a3d57610a358989838181106109f6576109f6614877565b9050602002013588888885818110610a1057610a10614877565b90506020020135878786818110610a2957610a29614877565b905060200201356135ab565b6001016109d9565b5050600160025550505050505050565b60105460ff1615610a7157604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff16158015610a9c57506000546001600160a01b03163314155b15610aba5760405163015783e960e51b815260040160405180910390fd5b600280541415610adc5760405162461bcd60e51b815260040161096e9061488d565b6002805562093a80610aee8185614836565b610af89190614858565b9250610b06848484846135ab565b505060016002555050565b60105460ff1615610b3557604051630f8eeedb60e01b815260040160405180910390fd5b600280541415610b575760405162461bcd60e51b815260040161096e9061488d565b600280556003548410610b7d57604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b03163314610bb45760405163015783e960e51b815260040160405180910390fd5b811580610bbf575080155b15610bdd57604051630e5a744960e41b815260040160405180910390fd5b65ffffffffffff8316610c035760405163641330f760e11b815260040160405180910390fd5b600084815260056020526040902054610c2f5760405163b31fbda360e01b815260040160405180910390fd5b60008481526005602052604081208054610c4b906001906148c4565b81548110610c5b57610c5b614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff169050610c956107d5565b811015610cb8576040516001622244ff60e11b0319815260040160405180910390fd5b600085815260066020908152604080832084845290915290205483610ce565ffffffffffff871683614858565b14610d0357604051636b5545d760e01b815260040160405180910390fd5b8261271060095486610d159190614858565b610d1f9190614836565b14610d3d57604051631fbbed9560e01b815260040160405180910390fd5b6000868152600460205260409020600101546001600160a01b0316610d6481333088613745565b600b54610d80906001600160a01b038381169133911687613745565b600062093a8080610d9181876148db565b610d9b9190614836565b610da59190614858565b905085600460008a81526020019081526020016000206003016000828254610dcd91906148db565b909155505060008881526004602052604090206002018054889190601490610e05908490600160a01b900465ffffffffffff166148f3565b825465ffffffffffff9182166101009390930a9283029190920219909116179055506000888152600660209081526040808320878452909152812060028101546001919091015490915b8965ffffffffffff16811015610f595760008481526007602090815260408083208054600181018255908452828420018e90558d835260059091529020610e95856137b6565b81546001810183556000928352602090922060058084049091018054919093066006026101000a65ffffffffffff818102199092169290911602179055610edb846137b6565b60008c8152600660209081526040808320888452909152902060058101805460028301879055600183018690559189905565ffffffffffff9290921666ffffffffffffff1990911617600160301b17905562093a8080610f3b81876148db565b610f459190614836565b610f4f9190614858565b9350600101610e4f565b506040805165ffffffffffff8b168152602081018a90528b917f41e2a6b551ac49e1ae233e45632ed6f80b37f725ab941c3bb604a7f9cdfbe28d910160405180910390a2505060016002555050505050505050565b600280541415610fd05760405162461bcd60e51b815260040161096e9061488d565b6002805560105460ff16610ff75760405163103c437960e31b815260040160405180910390fd5b6212750060115461100891906148db565b421015611028576040516324dab61b60e01b815260040160405180910390fd5b600354821061104a57604051630316876760e51b815260040160405180910390fd5b6000828152600460205260409020546001600160a01b031633146110815760405163015783e960e51b815260040160405180910390fd5b6001600160a01b0381166110a85760405163d92e233d60e01b815260040160405180910390fd5b60008281526005602090815260408083208054825181850281018501909352808352849383018282801561112b57602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff16815260200190600601906020826005010492830192600103820291508084116110ea5790505b505083519394506000925050505b818110156111fe5760008681526006602052604081208451829086908590811061116557611165614877565b602002602001015165ffffffffffff1681526020019081526020016000209050600160038111156111985761119861458b565b6005820154600160301b900460ff1660038111156111b8576111b861458b565b146111e257600481015480156111dc576111d281876148db565b6000600484015595505b506111f5565b80546111ee90866148db565b6000825594505b50600101611139565b508215611272576000858152600460205260409020600101546001600160a01b031661122b8186866137e3565b604080516001600160a01b03871681526020810186905287917f947a9dc0c5e62cc9756634ec0a89afea37eb0305933925040b9bda820044002091015b60405180910390a2505b50506001600255505050565b6000546001600160a01b031633146112a85760405162461bcd60e51b815260040161096e9061491d565b806112c657604051630309cb8760e51b815260040160405180910390fd5b600a80549082905560408051828152602081018490527f3933dd2114106692c792a24b4d8509cf9c5e4a41a71315c9f2b593085ea76e8991015b60405180910390a15050565b6005602052816000526040600020818154811061132857600080fd5b9060005260206000209060059182820401919006600602915091509054906101000a900465ffffffffffff1681565b6000818152600560205260408120546060918167ffffffffffffffff81111561138257611382614952565b6040519080825280602002602001820160405280156113bb57816020015b6113a8614218565b8152602001906001900390816113a05790505b50905060005b828110156114d3576000858152600660209081526040808320600590925282208054919291849081106113f6576113f6614877565b60009182526020808320600580840490910154928190066006026101000a90920465ffffffffffff9081168552848201959095526040938401909220835160e081018552815481526001820154938101939093526002810154938301939093526003808401546060840152600484015460808401529083015493841660a0830152909260c0840191600160301b90910460ff16908111156114995761149961458b565b60038111156114aa576114aa61458b565b815250508282815181106114c0576114c0614877565b60209081029190910101526001016113c1565b509392505050565b6000546001600160a01b031633146115055760405162461bcd60e51b815260040161096e9061491d565b61150f6000613818565b565b601054600090819060ff161561153a57604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff1615801561156557506000546001600160a01b03163314155b156115835760405163015783e960e51b815260040160405180910390fd5b6002805414156115a55760405162461bcd60e51b815260040161096e9061488d565b6002805562093a806115b78185614836565b6115c19190614858565b600c549093506001600160a01b03166115ed5760405163dc1669a360e01b815260040160405180910390fd5b8261160b576040516302e8f35960e31b815260040160405180910390fd5b6116136107d5565b8310611632576040516326f3d1bd60e21b815260040160405180910390fd5b60008381526007602052604090205461165e57604051637ddff0db60e11b815260040160405180910390fd5b6000838152600760209081526040808320805482518185028101850190935280835291929091908301828280156116b457602002820191906000526020600020905b8154815260200190600101908083116116a0575b505083519394506000925050505b818110156117205760006116ef878584815181106116e2576116e2614877565b6020026020010151613868565b90508015611709578561170181614968565b965050611717565b8461171381614968565b9550505b506001016116c2565b50506001600255509092909150565b6001546001600160a01b0316331461175a576040516305e05b4b60e31b815260040160405180910390fd5b6001546001600160a01b031661176f81613818565b600180546001600160a01b03191690556040516000906001600160a01b038316907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b908390a350565b60105460ff16156117dc57604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156117fe5760405162461bcd60e51b815260040161096e9061488d565b60028055600354841061182457604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b0316331461185b5760405163015783e960e51b815260040160405180910390fd5b821580611866575081155b8061186f575080155b1561188d57604051630e5a744960e41b815260040160405180910390fd5b600061189885613d47565b9050806118bb576040516001622244ff60e11b0319815260040160405180910390fd5b600062093a806118c96107d5565b6118d391906148db565b60008781526006602090815260408083208484529091529020600101549091508511611912576040516354082c4d60e11b815260040160405180910390fd5b6000868152600660209081526040808320848452909152812060020154670de0b6b3a764000090611944908890614858565b61194e9190614836565b60008881526006602090815260408083208684529091528120549192509061197690836148c4565b9050856119838583614858565b146119a157604051636b5545d760e01b815260040160405180910390fd5b84612710600954886119b39190614858565b6119bd9190614836565b146119db57604051631fbbed9560e01b815260040160405180910390fd5b6000888152600460205260409020600101546001600160a01b0316611a028133308a613745565b600b54611a1e906001600160a01b038381169133911689613745565b6000898152600560205260408120805486929190611a3e906001906148c4565b81548110611a4e57611a4e614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff16905088600460008d81526020019081526020016000206003016000828254611aa691906148db565b90915550600090505b87811015611b145781831115611ac457611b14565b60008c81526006602090815260408083208684529091529020600181018c905586905562093a8080611af681866148db565b611b009190614836565b611b0a9190614858565b9250600101611aaf565b50604080518b8152602081018b905287918d917fc02d0c6af9c33d6aa53748089cf0d89364cdfa21b95d4f727212b1707690ad7091015b60405180910390a350506001600255505050505050505050565b60105460ff1615611b8957604051630f8eeedb60e01b815260040160405180910390fd5b600280541415611bab5760405162461bcd60e51b815260040161096e9061488d565b600280556003548210611bd157604051630316876760e51b815260040160405180910390fd5b6000828152600460205260409020546001600160a01b03163314611c085760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038116611c2f5760405163d92e233d60e01b815260040160405180910390fd5b600082815260056020908152604080832080548251818502810185019093528083528493830182828015611cb257602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff1681526020019060060190602082600501049283019260010382029150808411611c715790505b505083519394506000925050505b81811015611d7757600086815260066020526040812084518290869085908110611cec57611cec614877565b602002602001015165ffffffffffff168152602001908152602001600020905060016003811115611d1f57611d1f61458b565b6005820154600160301b900460ff166003811115611d3f57611d3f61458b565b1415611d4e5750600101611cc0565b60048101548015611d6d57611d6381876148db565b6000600484015595505b5050600101611cc0565b508215611272576000858152600460205260409020600101546001600160a01b0316611da48186866137e3565b604080516001600160a01b03871681526020810186905287917f182587d643fe0f1dce1eafe8f758d78a2cfe0f9acdd4653ec0ad97dc94c3102d9101611268565b600080546001600160a01b03163314611e105760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0382166000908152600e602052604090205460ff1615611e4a576040516380eb2a0160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611e91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb59190614983565b905080611ed557604051630e5a744960e41b815260040160405180910390fd5b611efb611eea6000546001600160a01b031690565b6001600160a01b03851690836137e3565b60019150505b919050565b6000546001600160a01b03163314611f305760405162461bcd60e51b815260040161096e9061491d565b6101f4811115611f5357604051630309cb8760e51b815260040160405180910390fd5b600980549082905560408051828152602081018490527fd347e206f25a89b917fc9482f1a2d294d749baa4dc9bde7fb495ee11fe4916439101611300565b6000546001600160a01b03163314611fbb5760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116611fe25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152600d6020526040808220805460ff19169055517f0153940c4f400a39a1ca7f4cd7e46be4b7d3793f6142184e505b8c4e1a8103789190a250565b60105460009060ff161561205257604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156120745760405162461bcd60e51b815260040161096e9061488d565b60028055600c546001600160a01b03166120a15760405163dc1669a360e01b815260040160405180910390fd5b6120ce604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b3381526001600160a01b03891615806120ee57506001600160a01b038816155b1561210c5760405163d92e233d60e01b815260040160405180910390fd5b604051633f9095b760e01b81526001600160a01b038a811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690633f9095b790602401602060405180830381865afa158015612177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219b919061499c565b600f0b12156121bd576040516365da5bb960e11b815260040160405180910390fd5b6001600160a01b0388166000908152600e602052604090205460ff166121f6576040516307c241ad60e51b815260040160405180910390fd5b65ffffffffffff871661221c57604051631a82807360e21b815260040160405180910390fd5b600a5486101561223f576040516385f0de9f60e01b815260040160405180910390fd5b84158061224a575083155b80612253575082155b1561227157604051630e5a744960e41b815260040160405180910390fd5b6001600160a01b0388166000908152600f60205260409020548510156122aa5760405163059ffd6f60e41b815260040160405180910390fd5b670de0b6b3a76400006122bd8688614858565b6122c79190614836565b6020820181905284906122e39065ffffffffffff8a1690614858565b14612301576040516319d9907560e21b815260040160405180910390fd5b82612710600954866123139190614858565b61231d9190614836565b1461233b57604051631fbbed9560e01b815260040160405180910390fd5b8051612353906001600160a01b038a16903087613745565b8051600b5461236f916001600160a01b038b8116921686613745565b62093a8061237b6107d5565b61238591906148db565b816040018181525050600060035490506003600081546001019190508190555081600001516004600083815260200190815260200160002060000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550886004600083815260200190815260200160002060010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550896004600083815260200190815260200160002060020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550876004600083815260200190815260200160002060020160146101000a81548165ffffffffffff021916908365ffffffffffff1602179055508460046000838152602001908152602001600020600301819055506124bb82604001516137b6565b600082815260046020526040812060020180546001600160d01b0316600160d01b65ffffffffffff9485160217905590891667ffffffffffffffff81111561250557612505614952565b60405190808252806020026020018201604052801561252e578160200160208202803683370190505b50600c5460008481526008602052604080822080546001600160a01b0319166001600160a01b0390941693909317909255908501519192505b8a65ffffffffffff16811015612727576000828152600760209081526040822080546001810182559083529120018490556125a1826137b6565b8382815181106125b3576125b3614877565b602002602001019065ffffffffffff16908165ffffffffffff16815250506125da826137b6565b60066000868152602001908152602001600020600084815260200190815260200160002060050160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555089600660008681526020019081526020016000206000848152602001908152602001600020600201819055508860066000868152602001908152602001600020600084815260200190815260200160002060010181905550846020015160066000868152602001908152602001600020600084815260200190815260200160002060000181905550600160066000868152602001908152602001600020600084815260200190815260200160002060050160066101000a81548160ff021916908360038111156126f5576126f561458b565b021790555062093a808061270981856148db565b6127139190614836565b61271d9190614858565b9150600101612567565b50600083815260056020908152604090912083516127479285019061426d565b50600c5460405163162b501960e21b8152600481018590526001600160a01b038d81166024830152909116906358ad4064906044016020604051808303816000875af115801561279b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127bf91906149bf565b6127dc5760405163cc48ad7f60e01b815260040160405180910390fd5b835160408086015181516001600160a01b038f8116825265ffffffffffff8f16602083015292810191909152606081018c9052608081018b9052818f16929091169085907fbaf8f07da00833c4e270136cbbcb5a14339c232bebcb2962ca34fd45187963189060a00160405180910390a4505060016002559998505050505050505050565b6000546001600160a01b0316331461288b5760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0381166128b25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152600d6020526040808220805460ff19166001179055517f8e6f8d1a4c834c03bc5c8f9dedff078a6fd2904f7a94b17df04efb916aaba72d9190a250565b336000908152600d602052604090205460ff1615801561292957506000546001600160a01b03163314155b156129475760405163015783e960e51b815260040160405180910390fd5b6001600160a01b0382166000908152600e602052604090205460ff16612980576040516307c241ad60e51b815260040160405180910390fd5b8061299e57604051630309cb8760e51b815260040160405180910390fd5b6001600160a01b0382166000818152600f602052604090819020839055517f0b431c44bb4597805cf43550dbfc7dd3b2a6752c10eef974a53dba1c7e25ffa6906129eb9084815260200190565b60405180910390a25050565b6000546001600160a01b03163314612a215760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116612a485760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f111a961d91cf441fe07e7bfddc128b30ab56974d1a76851e969e0642fdb2dd509101611300565b336000908152600d602052604090205460ff16158015612acd57506000546001600160a01b03163314155b15612aeb5760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038216612b125760405163d92e233d60e01b815260040160405180910390fd5b80612b3057604051630309cb8760e51b815260040160405180910390fd5b6001600160a01b0382166000818152600e60209081526040808320805460ff19166001179055600f82529182902084905590518381527fb2fe06f7c1de1ec8e3329909cafefe4d305789d8438c9755943d504772d5373091016129eb565b6000546001600160a01b03163314612bb85760405162461bcd60e51b815260040161096e9061491d565b60105460ff16612bdb5760405163103c437960e31b815260040160405180910390fd5b62127500601154612bec91906148db565b4210612c0b5760405163c905adb160e01b815260040160405180910390fd5b6010805460ff191690556040514281527f62c91cdd7db0a8e05658629214f78fbbbc0708b9698bf57892027841dc8899c1906020015b60405180910390a1565b601054600090819060ff1615612c7457604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff16158015612c9f57506000546001600160a01b03163314155b15612cbd5760405163015783e960e51b815260040160405180910390fd5b600280541415612cdf5760405162461bcd60e51b815260040161096e9061488d565b6002805562093a80612cf18187614836565b612cfb9190614858565b94508280612d1c5760405163521299a960e01b815260040160405180910390fd5b600c546001600160a01b0316612d455760405163dc1669a360e01b815260040160405180910390fd5b85612d63576040516302e8f35960e31b815260040160405180910390fd5b612d6b6107d5565b8610612d8a576040516326f3d1bd60e21b815260040160405180910390fd5b600086815260076020526040902054612db657604051637ddff0db60e11b815260040160405180910390fd5b60005b81811015612e16576000612de588888885818110612dd957612dd9614877565b90506020020135613868565b90508015612dff5784612df781614968565b955050612e0d565b83612e0981614968565b9450505b50600101612db9565b505060016002559094909350915050565b6000546001600160a01b03163314612e515760405162461bcd60e51b815260040161096e9061491d565b60105460ff1615612e7557604051633d24012960e11b815260040160405180910390fd5b6010805460ff191660011790554260118190556040517ff4a991ad9e7f9711696f7bd41529beb4c470d75788573535d4ca3f0857c79ce891612c419190815260200190565b60076020528160005260406000208181548110612ed657600080fd5b90600052602060002001600091509150505481565b6000546001600160a01b03163314612f155760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116612f3c5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f9e1cbba858790e137077d865d7e4df5017a55524ea53a66b2c20ec665167d8d69101611300565b60008181526005602090815260409182902080548351818402810184019094528084526060939283018282801561301c57602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff1681526020019060060190602082600501049283019260010382029150808411612fdb5790505b50505050509050919050565b6000546001600160a01b031633146130525760405162461bcd60e51b815260040161096e9061491d565b600c546001600160a01b03161561307b5760405162dc149f60e41b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040519081527f48003ee9fdc82619ef4555b44a299acfd5cf34829def894201708b6bfea19ea19060200160405180910390a150565b6000546001600160a01b031633146130f95760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0381166131205760405163d92e233d60e01b815260040160405180910390fd5b6000546001600160a01b038281169116141561314f5760405163d5e889bf60e01b815260040160405180910390fd5b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b60105460ff16156131c557604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156131e75760405162461bcd60e51b815260040161096e9061488d565b60028055600354841061320d57604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b031633146132445760405163015783e960e51b815260040160405180910390fd5b81158061324f575080155b1561326d57604051630e5a744960e41b815260040160405180910390fd5b600061327885613d47565b90508061329b576040516001622244ff60e11b0319815260040160405180910390fd5b600062093a806132a96107d5565b6132b391906148db565b600087815260066020908152604080832084845290915290206002015490915085116132f25760405163ea543f3960e01b815260040160405180910390fd5b6000868152600660209081526040808320848452909152812060010154670de0b6b3a7640000906133239088614858565b61332d9190614836565b60008881526006602090815260408083208684529091528120549192509061335590836148c4565b9050856133628583614858565b1461338057604051636b5545d760e01b815260040160405180910390fd5b84612710600954886133929190614858565b61339c9190614836565b146133ba57604051631fbbed9560e01b815260040160405180910390fd5b6000888152600460205260409020600101546001600160a01b03166133e18133308a613745565b600b546133fd906001600160a01b038381169133911689613745565b600089815260056020526040812080548692919061341d906001906148c4565b8154811061342d5761342d614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff16905088600460008d8152602001908152602001600020600301600082825461348591906148db565b90915550600090505b878110156134f357818311156134a3576134f3565b60008c81526006602090815260408083208684529091529020600281018c905586905562093a80806134d581866148db565b6134df9190614836565b6134e99190614858565b925060010161348e565b50604080518b8152602081018b905287918d917f45fecdf1d9fa9009946a019cc9640fe30be20435de997c27a5fecfd5cfa26bc59101611b4b565b606062093a8061353e8184614836565b6135489190614858565b600081815260076020908152604091829020805483518184028101840190945280845293955091929083018282801561301c57602002820191906000526020600020905b81548152602001906001019080831161358c5750505050509050919050565b60035484106135cd57604051630316876760e51b815260040160405180910390fd5b806135eb576040516385ac2b9960e01b815260040160405180910390fd5b8161360957604051630e5a744960e41b815260040160405180910390fd5b60026000858152600660209081526040808320878452909152902060050154600160301b900460ff1660038111156136435761364361458b565b14613661576040516311372d3560e11b815260040160405180910390fd5b6000848152600860205260409081902054905162eba1a760e71b8152600481018690526024810185905260448101849052606481018390526001600160a01b03909116906375d0d380906084016020604051808303816000875af11580156136cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136f191906149bf565b61370e5760405163cc48ad7f60e01b815260040160405180910390fd5b505060009182526006602090815260408084209284529190529020600501805466ff00000000000019166603000000000000179055565b6040516001600160a01b03808516602483015283166044820152606481018290526137b09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e0c565b50505050565b600065ffffffffffff8211156137df5760405163fef46c2d60e01b815260040160405180910390fd5b5090565b6040516001600160a01b03831660248201526044810182905261381390849063a9059cbb60e01b90606401613779565b505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060016000838152600660209081526040808320878452909152902060050154600160301b900460ff1660038111156138a4576138a461458b565b146138b157506000613d41565b60006138c062093a80856148db565b905060007f000000000000000000000000000000000000000000000000000000000000000090506000600460008681526020019081526020016000206040518060c00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160149054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160028201601a9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff168152602001600382015481525050905060006006600087815260200190815260200160002060008881526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016005820160069054906101000a900460ff166003811115613aab57613aab61458b565b6003811115613abc57613abc61458b565b9052509050600260c0820181905250604082810151905163615e523760e01b81526001600160a01b0391821660048201529084169063615e523790602401600060405180830381600087803b158015613b1457600080fd5b505af1158015613b28573d6000803e3d6000fd5b505050506000613b3c836040015186613ede565b905080613b4f5781516080830152613c69565b60008260400151821015613b8557670de0b6b3a7640000836020015183613b769190614858565b613b809190614836565b613b88565b82515b606084018190528351909150613b9f9082906148c4565b60808401526000888152600860205260409081902054905163612d26cf60e11b8152600481018a9052602481018b9052604481018390526001600160a01b0390911690819063c25a4d9e906064016020604051808303816000875af1158015613c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3091906149bf565b613c4d5760405163cc48ad7f60e01b815260040160405180910390fd5b6020850151613c66906001600160a01b031682846137e3565b50505b60008781526006602090815260408083208b84528252918290208451815590840151600182015590830151600282015560608301516003808301919091556080840151600483015560a084015160058301805465ffffffffffff90921665ffffffffffff1983168117825560c0870151879594909366ffffffffffffff191690911790600160301b908490811115613d0357613d0361458b565b02179055505060405189915088907f8deaacba1e0606cd9e75aec4cb0fefc4c1df8d262d3a05f83107f8c067e8891f90600090a36001955050505050505b92915050565b600081815260056020526040812054613d735760405163b31fbda360e01b815260040160405180910390fd5b60008281526005602052604081208054613d8f906001906148c4565b81548110613d9f57613d9f614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff1690506000613ddb6107d5565b9050808210613e015762093a80613df282846148c4565b613dfc9190614836565b613e04565b60005b949350505050565b6000613e61826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166140959092919063ffffffff16565b8051909150156138135780806020019051810190613e7f91906149bf565b6138135760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161096e565b6040516303f20c7d60e51b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015283811660248301526000917f000000000000000000000000000000000000000000000000000000000000000091839190831690637e418fa090604401602060405180830381865afa158015613f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f989190614983565b6040516301e8cff360e31b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301528781166024830152919250600091841690630f467f9890604401606060405180830381865afa15801561400c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403091906149e1565b9050848211156140465760009350505050613d41565b8481604001511161405d5760009350505050613d41565b805161406f5760009350505050613d41565b84816040015161407f91906148c4565b815161408b9190614858565b9695505050505050565b60606140a484846000856140ae565b90505b9392505050565b60608247101561410f5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161096e565b6001600160a01b0385163b6141665760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161096e565b600080866001600160a01b031685876040516141829190614a77565b60006040518083038185875af1925050503d80600081146141bf576040519150601f19603f3d011682016040523d82523d6000602084013e6141c4565b606091505b50915091506141d48282866141df565b979650505050505050565b606083156141ee5750816140a7565b8251156141fe5782518084602001fd5b8160405162461bcd60e51b815260040161096e9190614a93565b6040518060e001604052806000815260200160008152602001600081526020016000815260200160008152602001600065ffffffffffff168152602001600060038111156142685761426861458b565b905290565b828054828255906000526020600020906004016005900481019282156143125791602002820160005b838211156142de57835183826101000a81548165ffffffffffff021916908365ffffffffffff1602179055509260200192600601602081600501049283019260010302614296565b80156143105782816101000a81549065ffffffffffff02191690556006016020816005010492830192600103026142de565b505b506137df9291505b808211156137df576000815560010161431a565b60008083601f84011261434057600080fd5b50813567ffffffffffffffff81111561435857600080fd5b6020830191508360208260051b850101111561437357600080fd5b9250929050565b6000806000806040858703121561439057600080fd5b843567ffffffffffffffff808211156143a857600080fd5b6143b48883890161432e565b909650945060208701359150808211156143cd57600080fd5b506143da8782880161432e565b95989497509550505050565b60008060008060008060006080888a03121561440157600080fd5b873567ffffffffffffffff8082111561441957600080fd5b6144258b838c0161432e565b909950975060208a0135965060408a013591508082111561444557600080fd5b6144518b838c0161432e565b909650945060608a013591508082111561446a57600080fd5b506144778a828b0161432e565b989b979a50959850939692959293505050565b600080600080608085870312156144a057600080fd5b5050823594602084013594506040840135936060013592509050565b803565ffffffffffff81168114611f0157600080fd5b600080600080608085870312156144e857600080fd5b843593506144f8602086016144bc565b93969395505050506040820135916060013590565b80356001600160a01b0381168114611f0157600080fd5b6000806040838503121561453757600080fd5b823591506145476020840161450d565b90509250929050565b60006020828403121561456257600080fd5b5035919050565b6000806040838503121561457c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b600481106145bf57634e487b7160e01b600052602160045260246000fd5b9052565b602080825282518282018190526000919060409081850190868401855b8281101561464c5781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a08082015165ffffffffffff169086015260c09081015190614637818701836145a1565b505060e09390930192908501906001016145e0565b5091979650505050505050565b60006020828403121561466b57600080fd5b6140a78261450d565b600080600080600080600060e0888a03121561468f57600080fd5b6146988861450d565b96506146a66020890161450d565b95506146b4604089016144bc565b969995985095966060810135965060808101359560a0820135955060c0909101359350915050565b600060e08201905088825287602083015286604083015285606083015284608083015265ffffffffffff841660a083015261471a60c08301846145a1565b98975050505050505050565b6000806040838503121561473957600080fd5b6147428361450d565b946020939093013593505050565b60008060006040848603121561476557600080fd5b83359250602084013567ffffffffffffffff81111561478357600080fd5b61478f8682870161432e565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b818110156147dc57835165ffffffffffff16835292840192918401916001016147b8565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156147dc57835183529284019291840191600101614804565b634e487b7160e01b600052601160045260246000fd5b60008261485357634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561487257614872614820565b500290565b634e487b7160e01b600052603260045260246000fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6000828210156148d6576148d6614820565b500390565b600082198211156148ee576148ee614820565b500190565b600065ffffffffffff80831681851680830382111561491457614914614820565b01949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b600060001982141561497c5761497c614820565b5060010190565b60006020828403121561499557600080fd5b5051919050565b6000602082840312156149ae57600080fd5b815180600f0b81146140a757600080fd5b6000602082840312156149d157600080fd5b815180151581146140a757600080fd5b6000606082840312156149f357600080fd5b6040516060810181811067ffffffffffffffff82111715614a2457634e487b7160e01b600052604160045260246000fd5b80604052508251815260208301516020820152604083015160408201528091505092915050565b60005b83811015614a66578181015183820152602001614a4e565b838111156137b05750506000910152565b60008251614a89818460208701614a4b565b9190910192915050565b6020815260008251806020840152614ab2816040850160208701614a4b565b601f01601f1916919091016040019291505056fea2646970667358221220adf045b10094b82b9dc74fc0d4d0b658123db17c6ff26e57dcc435722b913eed64736f6c634300080a0033000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221800000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b0000000000000000000000000482a2d6e2f895125b7237de70c675cd55fe17ca
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102bb5760003560e01c8063a78d0fbd11610182578063d52f1135116100e9578063eca5d0ca116100a2578063f2fde38b1161007c578063f2fde38b14610786578063f634f8d514610799578063f83d658b146107a2578063f9981140146107b557600080fd5b8063eca5d0ca14610740578063f29e9aa714610753578063f2ed9db61461077357600080fd5b8063d52f113514610634578063daf9c21014610647578063dc34a1511461066a578063e085f98014610672578063e30c39781461071a578063e928ef271461072d57600080fd5b8063ba82c8971161013b578063ba82c897146105d6578063bc30a618146105e9578063bfe10928146105fc578063c4e16b7d1461060f578063c6c02cac14610622578063ce7946b61461062a57600080fd5b8063a78d0fbd1461050c578063aa0b598814610515578063ac18de4314610528578063b3190eb91461053b578063b5eee7221461054e578063ba28facd1461056157600080fd5b806345c46147116102265780637f0301b2116101df5780637f0301b2146104925780638da5cb5b146104a55780638f4c72d8146104b65780638fe8a101146104c95780639e8c708e146104e6578063a6c93d7d146104f957600080fd5b806345c46147146103f05780636f42e67f1461041a578063715018a61461043a5780637211a52b1461044257806379ba50971461046a57806379dc77d01461047257600080fd5b806322884e2c1161027857806322884e2c1461035e578063255e7d1a1461038557806326232a2e146103985780632f940c70146103a15780633d723877146103b45780633da8a8d1146103c757600080fd5b8063086146d2146102c05780630881b830146102db57806311195485146102f057806319d227e81461030357806319e0fe1f146103425780631e96917d14610355575b600080fd5b6102c86107d5565b6040519081526020015b60405180910390f35b6102ee6102e936600461437a565b6107f4565b005b6102ee6102fe3660046143e6565b6108df565b61032a7f00000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b81565b6040516001600160a01b0390911681526020016102d2565b6102ee61035036600461448a565b610a4d565b6102c860035481565b61032a7f000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221881565b6102ee6103933660046144d2565b610b11565b6102c860095481565b6102ee6103af366004614524565b610fae565b6102ee6103c2366004614550565b61127e565b61032a6103d5366004614550565b6008602052600090815260409020546001600160a01b031681565b6104036103fe366004614569565b61130c565b60405165ffffffffffff90911681526020016102d2565b61042d610428366004614550565b611357565b6040516102d291906145c3565b6102ee6114db565b610455610450366004614550565b611511565b604080519283526020830191909152016102d2565b6102ee61172f565b6102c8610480366004614659565b600f6020526000908152604090205481565b6102ee6104a036600461448a565b6117b8565b6000546001600160a01b031661032a565b6102ee6104c4366004614524565b611b65565b6010546104d69060ff1681565b60405190151581526020016102d2565b6104d66104f4366004614659565b611de5565b600b5461032a906001600160a01b031681565b6102c860115481565b6102ee610523366004614550565b611f06565b6102ee610536366004614659565b611f91565b6102c8610549366004614674565b61202b565b6102ee61055c366004614659565b612861565b6105c361056f366004614569565b6006602090815260009283526040808420909152908252902080546001820154600283015460038401546004850154600590950154939492939192909165ffffffffffff811690600160301b900460ff1687565b6040516102d297969594939291906146dc565b6102ee6105e4366004614726565b6128fe565b6102ee6105f7366004614659565b6129f7565b600c5461032a906001600160a01b031681565b6102ee61061d366004614726565b612aa2565b6102ee612b8e565b6102c86212750081565b610455610642366004614750565b612c4b565b6104d6610655366004614659565b600e6020526000908152604090205460ff1681565b6102ee612e27565b6106d1610680366004614550565b60046020526000908152604090208054600182015460028301546003909301546001600160a01b03928316939183169282169165ffffffffffff600160a01b8204811692600160d01b909204169086565b604080516001600160a01b0397881681529587166020870152939095169284019290925265ffffffffffff908116606084015216608082015260a081019190915260c0016102d2565b60015461032a906001600160a01b031681565b6102c861073b366004614569565b612eba565b6102ee61074e366004614659565b612eeb565b610766610761366004614550565b612f96565b6040516102d2919061479c565b6102ee610781366004614659565b613028565b6102ee610794366004614659565b6130cf565b6102c8600a5481565b6102ee6107b036600461448a565b6131a1565b6107c86107c3366004614550565b61352e565b6040516102d291906147e8565b600062093a806107e58142614836565b6107ef9190614858565b905090565b336000908152600d602052604090205460ff1615801561081f57506000546001600160a01b03163314155b1561083d5760405163015783e960e51b815260040160405180910390fd5b828061085c5760405163521299a960e01b815260040160405180910390fd5b80821461087c5760405163c155fe2160e01b815260040160405180910390fd5b60005b818110156108d7576108cf86868381811061089c5761089c614877565b90506020020160208101906108b19190614659565b8585848181106108c3576108c3614877565b90506020020135612aa2565b60010161087f565b505050505050565b60105460ff161561090357604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff1615801561092e57506000546001600160a01b03163314155b1561094c5760405163015783e960e51b815260040160405180910390fd5b6002805414156109775760405162461bcd60e51b815260040161096e9061488d565b60405180910390fd5b6002805562093a806109898187614836565b6109939190614858565b9450858181146109b65760405163c155fe2160e01b815260040160405180910390fd5b8084146109d65760405163c155fe2160e01b815260040160405180910390fd5b60005b81811015610a3d57610a358989838181106109f6576109f6614877565b9050602002013588888885818110610a1057610a10614877565b90506020020135878786818110610a2957610a29614877565b905060200201356135ab565b6001016109d9565b5050600160025550505050505050565b60105460ff1615610a7157604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff16158015610a9c57506000546001600160a01b03163314155b15610aba5760405163015783e960e51b815260040160405180910390fd5b600280541415610adc5760405162461bcd60e51b815260040161096e9061488d565b6002805562093a80610aee8185614836565b610af89190614858565b9250610b06848484846135ab565b505060016002555050565b60105460ff1615610b3557604051630f8eeedb60e01b815260040160405180910390fd5b600280541415610b575760405162461bcd60e51b815260040161096e9061488d565b600280556003548410610b7d57604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b03163314610bb45760405163015783e960e51b815260040160405180910390fd5b811580610bbf575080155b15610bdd57604051630e5a744960e41b815260040160405180910390fd5b65ffffffffffff8316610c035760405163641330f760e11b815260040160405180910390fd5b600084815260056020526040902054610c2f5760405163b31fbda360e01b815260040160405180910390fd5b60008481526005602052604081208054610c4b906001906148c4565b81548110610c5b57610c5b614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff169050610c956107d5565b811015610cb8576040516001622244ff60e11b0319815260040160405180910390fd5b600085815260066020908152604080832084845290915290205483610ce565ffffffffffff871683614858565b14610d0357604051636b5545d760e01b815260040160405180910390fd5b8261271060095486610d159190614858565b610d1f9190614836565b14610d3d57604051631fbbed9560e01b815260040160405180910390fd5b6000868152600460205260409020600101546001600160a01b0316610d6481333088613745565b600b54610d80906001600160a01b038381169133911687613745565b600062093a8080610d9181876148db565b610d9b9190614836565b610da59190614858565b905085600460008a81526020019081526020016000206003016000828254610dcd91906148db565b909155505060008881526004602052604090206002018054889190601490610e05908490600160a01b900465ffffffffffff166148f3565b825465ffffffffffff9182166101009390930a9283029190920219909116179055506000888152600660209081526040808320878452909152812060028101546001919091015490915b8965ffffffffffff16811015610f595760008481526007602090815260408083208054600181018255908452828420018e90558d835260059091529020610e95856137b6565b81546001810183556000928352602090922060058084049091018054919093066006026101000a65ffffffffffff818102199092169290911602179055610edb846137b6565b60008c8152600660209081526040808320888452909152902060058101805460028301879055600183018690559189905565ffffffffffff9290921666ffffffffffffff1990911617600160301b17905562093a8080610f3b81876148db565b610f459190614836565b610f4f9190614858565b9350600101610e4f565b506040805165ffffffffffff8b168152602081018a90528b917f41e2a6b551ac49e1ae233e45632ed6f80b37f725ab941c3bb604a7f9cdfbe28d910160405180910390a2505060016002555050505050505050565b600280541415610fd05760405162461bcd60e51b815260040161096e9061488d565b6002805560105460ff16610ff75760405163103c437960e31b815260040160405180910390fd5b6212750060115461100891906148db565b421015611028576040516324dab61b60e01b815260040160405180910390fd5b600354821061104a57604051630316876760e51b815260040160405180910390fd5b6000828152600460205260409020546001600160a01b031633146110815760405163015783e960e51b815260040160405180910390fd5b6001600160a01b0381166110a85760405163d92e233d60e01b815260040160405180910390fd5b60008281526005602090815260408083208054825181850281018501909352808352849383018282801561112b57602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff16815260200190600601906020826005010492830192600103820291508084116110ea5790505b505083519394506000925050505b818110156111fe5760008681526006602052604081208451829086908590811061116557611165614877565b602002602001015165ffffffffffff1681526020019081526020016000209050600160038111156111985761119861458b565b6005820154600160301b900460ff1660038111156111b8576111b861458b565b146111e257600481015480156111dc576111d281876148db565b6000600484015595505b506111f5565b80546111ee90866148db565b6000825594505b50600101611139565b508215611272576000858152600460205260409020600101546001600160a01b031661122b8186866137e3565b604080516001600160a01b03871681526020810186905287917f947a9dc0c5e62cc9756634ec0a89afea37eb0305933925040b9bda820044002091015b60405180910390a2505b50506001600255505050565b6000546001600160a01b031633146112a85760405162461bcd60e51b815260040161096e9061491d565b806112c657604051630309cb8760e51b815260040160405180910390fd5b600a80549082905560408051828152602081018490527f3933dd2114106692c792a24b4d8509cf9c5e4a41a71315c9f2b593085ea76e8991015b60405180910390a15050565b6005602052816000526040600020818154811061132857600080fd5b9060005260206000209060059182820401919006600602915091509054906101000a900465ffffffffffff1681565b6000818152600560205260408120546060918167ffffffffffffffff81111561138257611382614952565b6040519080825280602002602001820160405280156113bb57816020015b6113a8614218565b8152602001906001900390816113a05790505b50905060005b828110156114d3576000858152600660209081526040808320600590925282208054919291849081106113f6576113f6614877565b60009182526020808320600580840490910154928190066006026101000a90920465ffffffffffff9081168552848201959095526040938401909220835160e081018552815481526001820154938101939093526002810154938301939093526003808401546060840152600484015460808401529083015493841660a0830152909260c0840191600160301b90910460ff16908111156114995761149961458b565b60038111156114aa576114aa61458b565b815250508282815181106114c0576114c0614877565b60209081029190910101526001016113c1565b509392505050565b6000546001600160a01b031633146115055760405162461bcd60e51b815260040161096e9061491d565b61150f6000613818565b565b601054600090819060ff161561153a57604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff1615801561156557506000546001600160a01b03163314155b156115835760405163015783e960e51b815260040160405180910390fd5b6002805414156115a55760405162461bcd60e51b815260040161096e9061488d565b6002805562093a806115b78185614836565b6115c19190614858565b600c549093506001600160a01b03166115ed5760405163dc1669a360e01b815260040160405180910390fd5b8261160b576040516302e8f35960e31b815260040160405180910390fd5b6116136107d5565b8310611632576040516326f3d1bd60e21b815260040160405180910390fd5b60008381526007602052604090205461165e57604051637ddff0db60e11b815260040160405180910390fd5b6000838152600760209081526040808320805482518185028101850190935280835291929091908301828280156116b457602002820191906000526020600020905b8154815260200190600101908083116116a0575b505083519394506000925050505b818110156117205760006116ef878584815181106116e2576116e2614877565b6020026020010151613868565b90508015611709578561170181614968565b965050611717565b8461171381614968565b9550505b506001016116c2565b50506001600255509092909150565b6001546001600160a01b0316331461175a576040516305e05b4b60e31b815260040160405180910390fd5b6001546001600160a01b031661176f81613818565b600180546001600160a01b03191690556040516000906001600160a01b038316907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b908390a350565b60105460ff16156117dc57604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156117fe5760405162461bcd60e51b815260040161096e9061488d565b60028055600354841061182457604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b0316331461185b5760405163015783e960e51b815260040160405180910390fd5b821580611866575081155b8061186f575080155b1561188d57604051630e5a744960e41b815260040160405180910390fd5b600061189885613d47565b9050806118bb576040516001622244ff60e11b0319815260040160405180910390fd5b600062093a806118c96107d5565b6118d391906148db565b60008781526006602090815260408083208484529091529020600101549091508511611912576040516354082c4d60e11b815260040160405180910390fd5b6000868152600660209081526040808320848452909152812060020154670de0b6b3a764000090611944908890614858565b61194e9190614836565b60008881526006602090815260408083208684529091528120549192509061197690836148c4565b9050856119838583614858565b146119a157604051636b5545d760e01b815260040160405180910390fd5b84612710600954886119b39190614858565b6119bd9190614836565b146119db57604051631fbbed9560e01b815260040160405180910390fd5b6000888152600460205260409020600101546001600160a01b0316611a028133308a613745565b600b54611a1e906001600160a01b038381169133911689613745565b6000898152600560205260408120805486929190611a3e906001906148c4565b81548110611a4e57611a4e614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff16905088600460008d81526020019081526020016000206003016000828254611aa691906148db565b90915550600090505b87811015611b145781831115611ac457611b14565b60008c81526006602090815260408083208684529091529020600181018c905586905562093a8080611af681866148db565b611b009190614836565b611b0a9190614858565b9250600101611aaf565b50604080518b8152602081018b905287918d917fc02d0c6af9c33d6aa53748089cf0d89364cdfa21b95d4f727212b1707690ad7091015b60405180910390a350506001600255505050505050505050565b60105460ff1615611b8957604051630f8eeedb60e01b815260040160405180910390fd5b600280541415611bab5760405162461bcd60e51b815260040161096e9061488d565b600280556003548210611bd157604051630316876760e51b815260040160405180910390fd5b6000828152600460205260409020546001600160a01b03163314611c085760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038116611c2f5760405163d92e233d60e01b815260040160405180910390fd5b600082815260056020908152604080832080548251818502810185019093528083528493830182828015611cb257602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff1681526020019060060190602082600501049283019260010382029150808411611c715790505b505083519394506000925050505b81811015611d7757600086815260066020526040812084518290869085908110611cec57611cec614877565b602002602001015165ffffffffffff168152602001908152602001600020905060016003811115611d1f57611d1f61458b565b6005820154600160301b900460ff166003811115611d3f57611d3f61458b565b1415611d4e5750600101611cc0565b60048101548015611d6d57611d6381876148db565b6000600484015595505b5050600101611cc0565b508215611272576000858152600460205260409020600101546001600160a01b0316611da48186866137e3565b604080516001600160a01b03871681526020810186905287917f182587d643fe0f1dce1eafe8f758d78a2cfe0f9acdd4653ec0ad97dc94c3102d9101611268565b600080546001600160a01b03163314611e105760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0382166000908152600e602052604090205460ff1615611e4a576040516380eb2a0160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015611e91573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611eb59190614983565b905080611ed557604051630e5a744960e41b815260040160405180910390fd5b611efb611eea6000546001600160a01b031690565b6001600160a01b03851690836137e3565b60019150505b919050565b6000546001600160a01b03163314611f305760405162461bcd60e51b815260040161096e9061491d565b6101f4811115611f5357604051630309cb8760e51b815260040160405180910390fd5b600980549082905560408051828152602081018490527fd347e206f25a89b917fc9482f1a2d294d749baa4dc9bde7fb495ee11fe4916439101611300565b6000546001600160a01b03163314611fbb5760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116611fe25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152600d6020526040808220805460ff19169055517f0153940c4f400a39a1ca7f4cd7e46be4b7d3793f6142184e505b8c4e1a8103789190a250565b60105460009060ff161561205257604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156120745760405162461bcd60e51b815260040161096e9061488d565b60028055600c546001600160a01b03166120a15760405163dc1669a360e01b815260040160405180910390fd5b6120ce604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b3381526001600160a01b03891615806120ee57506001600160a01b038816155b1561210c5760405163d92e233d60e01b815260040160405180910390fd5b604051633f9095b760e01b81526001600160a01b038a811660048301526000917f000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221890911690633f9095b790602401602060405180830381865afa158015612177573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061219b919061499c565b600f0b12156121bd576040516365da5bb960e11b815260040160405180910390fd5b6001600160a01b0388166000908152600e602052604090205460ff166121f6576040516307c241ad60e51b815260040160405180910390fd5b65ffffffffffff871661221c57604051631a82807360e21b815260040160405180910390fd5b600a5486101561223f576040516385f0de9f60e01b815260040160405180910390fd5b84158061224a575083155b80612253575082155b1561227157604051630e5a744960e41b815260040160405180910390fd5b6001600160a01b0388166000908152600f60205260409020548510156122aa5760405163059ffd6f60e41b815260040160405180910390fd5b670de0b6b3a76400006122bd8688614858565b6122c79190614836565b6020820181905284906122e39065ffffffffffff8a1690614858565b14612301576040516319d9907560e21b815260040160405180910390fd5b82612710600954866123139190614858565b61231d9190614836565b1461233b57604051631fbbed9560e01b815260040160405180910390fd5b8051612353906001600160a01b038a16903087613745565b8051600b5461236f916001600160a01b038b8116921686613745565b62093a8061237b6107d5565b61238591906148db565b816040018181525050600060035490506003600081546001019190508190555081600001516004600083815260200190815260200160002060000160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550886004600083815260200190815260200160002060010160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550896004600083815260200190815260200160002060020160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550876004600083815260200190815260200160002060020160146101000a81548165ffffffffffff021916908365ffffffffffff1602179055508460046000838152602001908152602001600020600301819055506124bb82604001516137b6565b600082815260046020526040812060020180546001600160d01b0316600160d01b65ffffffffffff9485160217905590891667ffffffffffffffff81111561250557612505614952565b60405190808252806020026020018201604052801561252e578160200160208202803683370190505b50600c5460008481526008602052604080822080546001600160a01b0319166001600160a01b0390941693909317909255908501519192505b8a65ffffffffffff16811015612727576000828152600760209081526040822080546001810182559083529120018490556125a1826137b6565b8382815181106125b3576125b3614877565b602002602001019065ffffffffffff16908165ffffffffffff16815250506125da826137b6565b60066000868152602001908152602001600020600084815260200190815260200160002060050160006101000a81548165ffffffffffff021916908365ffffffffffff16021790555089600660008681526020019081526020016000206000848152602001908152602001600020600201819055508860066000868152602001908152602001600020600084815260200190815260200160002060010181905550846020015160066000868152602001908152602001600020600084815260200190815260200160002060000181905550600160066000868152602001908152602001600020600084815260200190815260200160002060050160066101000a81548160ff021916908360038111156126f5576126f561458b565b021790555062093a808061270981856148db565b6127139190614836565b61271d9190614858565b9150600101612567565b50600083815260056020908152604090912083516127479285019061426d565b50600c5460405163162b501960e21b8152600481018590526001600160a01b038d81166024830152909116906358ad4064906044016020604051808303816000875af115801561279b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127bf91906149bf565b6127dc5760405163cc48ad7f60e01b815260040160405180910390fd5b835160408086015181516001600160a01b038f8116825265ffffffffffff8f16602083015292810191909152606081018c9052608081018b9052818f16929091169085907fbaf8f07da00833c4e270136cbbcb5a14339c232bebcb2962ca34fd45187963189060a00160405180910390a4505060016002559998505050505050505050565b6000546001600160a01b0316331461288b5760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0381166128b25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0381166000818152600d6020526040808220805460ff19166001179055517f8e6f8d1a4c834c03bc5c8f9dedff078a6fd2904f7a94b17df04efb916aaba72d9190a250565b336000908152600d602052604090205460ff1615801561292957506000546001600160a01b03163314155b156129475760405163015783e960e51b815260040160405180910390fd5b6001600160a01b0382166000908152600e602052604090205460ff16612980576040516307c241ad60e51b815260040160405180910390fd5b8061299e57604051630309cb8760e51b815260040160405180910390fd5b6001600160a01b0382166000818152600f602052604090819020839055517f0b431c44bb4597805cf43550dbfc7dd3b2a6752c10eef974a53dba1c7e25ffa6906129eb9084815260200190565b60405180910390a25050565b6000546001600160a01b03163314612a215760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116612a485760405163d92e233d60e01b815260040160405180910390fd5b600c80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f111a961d91cf441fe07e7bfddc128b30ab56974d1a76851e969e0642fdb2dd509101611300565b336000908152600d602052604090205460ff16158015612acd57506000546001600160a01b03163314155b15612aeb5760405163015783e960e51b815260040160405180910390fd5b6001600160a01b038216612b125760405163d92e233d60e01b815260040160405180910390fd5b80612b3057604051630309cb8760e51b815260040160405180910390fd5b6001600160a01b0382166000818152600e60209081526040808320805460ff19166001179055600f82529182902084905590518381527fb2fe06f7c1de1ec8e3329909cafefe4d305789d8438c9755943d504772d5373091016129eb565b6000546001600160a01b03163314612bb85760405162461bcd60e51b815260040161096e9061491d565b60105460ff16612bdb5760405163103c437960e31b815260040160405180910390fd5b62127500601154612bec91906148db565b4210612c0b5760405163c905adb160e01b815260040160405180910390fd5b6010805460ff191690556040514281527f62c91cdd7db0a8e05658629214f78fbbbc0708b9698bf57892027841dc8899c1906020015b60405180910390a1565b601054600090819060ff1615612c7457604051630f8eeedb60e01b815260040160405180910390fd5b336000908152600d602052604090205460ff16158015612c9f57506000546001600160a01b03163314155b15612cbd5760405163015783e960e51b815260040160405180910390fd5b600280541415612cdf5760405162461bcd60e51b815260040161096e9061488d565b6002805562093a80612cf18187614836565b612cfb9190614858565b94508280612d1c5760405163521299a960e01b815260040160405180910390fd5b600c546001600160a01b0316612d455760405163dc1669a360e01b815260040160405180910390fd5b85612d63576040516302e8f35960e31b815260040160405180910390fd5b612d6b6107d5565b8610612d8a576040516326f3d1bd60e21b815260040160405180910390fd5b600086815260076020526040902054612db657604051637ddff0db60e11b815260040160405180910390fd5b60005b81811015612e16576000612de588888885818110612dd957612dd9614877565b90506020020135613868565b90508015612dff5784612df781614968565b955050612e0d565b83612e0981614968565b9450505b50600101612db9565b505060016002559094909350915050565b6000546001600160a01b03163314612e515760405162461bcd60e51b815260040161096e9061491d565b60105460ff1615612e7557604051633d24012960e11b815260040160405180910390fd5b6010805460ff191660011790554260118190556040517ff4a991ad9e7f9711696f7bd41529beb4c470d75788573535d4ca3f0857c79ce891612c419190815260200190565b60076020528160005260406000208181548110612ed657600080fd5b90600052602060002001600091509150505481565b6000546001600160a01b03163314612f155760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b038116612f3c5760405163d92e233d60e01b815260040160405180910390fd5b600b80546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f9e1cbba858790e137077d865d7e4df5017a55524ea53a66b2c20ec665167d8d69101611300565b60008181526005602090815260409182902080548351818402810184019094528084526060939283018282801561301c57602002820191906000526020600020906000905b82829054906101000a900465ffffffffffff1665ffffffffffff1681526020019060060190602082600501049283019260010382029150808411612fdb5790505b50505050509050919050565b6000546001600160a01b031633146130525760405162461bcd60e51b815260040161096e9061491d565b600c546001600160a01b03161561307b5760405162dc149f60e41b815260040160405180910390fd5b600c80546001600160a01b0319166001600160a01b0383169081179091556040519081527f48003ee9fdc82619ef4555b44a299acfd5cf34829def894201708b6bfea19ea19060200160405180910390a150565b6000546001600160a01b031633146130f95760405162461bcd60e51b815260040161096e9061491d565b6001600160a01b0381166131205760405163d92e233d60e01b815260040160405180910390fd5b6000546001600160a01b038281169116141561314f5760405163d5e889bf60e01b815260040160405180910390fd5b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b60105460ff16156131c557604051630f8eeedb60e01b815260040160405180910390fd5b6002805414156131e75760405162461bcd60e51b815260040161096e9061488d565b60028055600354841061320d57604051630316876760e51b815260040160405180910390fd5b6000848152600460205260409020546001600160a01b031633146132445760405163015783e960e51b815260040160405180910390fd5b81158061324f575080155b1561326d57604051630e5a744960e41b815260040160405180910390fd5b600061327885613d47565b90508061329b576040516001622244ff60e11b0319815260040160405180910390fd5b600062093a806132a96107d5565b6132b391906148db565b600087815260066020908152604080832084845290915290206002015490915085116132f25760405163ea543f3960e01b815260040160405180910390fd5b6000868152600660209081526040808320848452909152812060010154670de0b6b3a7640000906133239088614858565b61332d9190614836565b60008881526006602090815260408083208684529091528120549192509061335590836148c4565b9050856133628583614858565b1461338057604051636b5545d760e01b815260040160405180910390fd5b84612710600954886133929190614858565b61339c9190614836565b146133ba57604051631fbbed9560e01b815260040160405180910390fd5b6000888152600460205260409020600101546001600160a01b03166133e18133308a613745565b600b546133fd906001600160a01b038381169133911689613745565b600089815260056020526040812080548692919061341d906001906148c4565b8154811061342d5761342d614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff16905088600460008d8152602001908152602001600020600301600082825461348591906148db565b90915550600090505b878110156134f357818311156134a3576134f3565b60008c81526006602090815260408083208684529091529020600281018c905586905562093a80806134d581866148db565b6134df9190614836565b6134e99190614858565b925060010161348e565b50604080518b8152602081018b905287918d917f45fecdf1d9fa9009946a019cc9640fe30be20435de997c27a5fecfd5cfa26bc59101611b4b565b606062093a8061353e8184614836565b6135489190614858565b600081815260076020908152604091829020805483518184028101840190945280845293955091929083018282801561301c57602002820191906000526020600020905b81548152602001906001019080831161358c5750505050509050919050565b60035484106135cd57604051630316876760e51b815260040160405180910390fd5b806135eb576040516385ac2b9960e01b815260040160405180910390fd5b8161360957604051630e5a744960e41b815260040160405180910390fd5b60026000858152600660209081526040808320878452909152902060050154600160301b900460ff1660038111156136435761364361458b565b14613661576040516311372d3560e11b815260040160405180910390fd5b6000848152600860205260409081902054905162eba1a760e71b8152600481018690526024810185905260448101849052606481018390526001600160a01b03909116906375d0d380906084016020604051808303816000875af11580156136cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136f191906149bf565b61370e5760405163cc48ad7f60e01b815260040160405180910390fd5b505060009182526006602090815260408084209284529190529020600501805466ff00000000000019166603000000000000179055565b6040516001600160a01b03808516602483015283166044820152606481018290526137b09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613e0c565b50505050565b600065ffffffffffff8211156137df5760405163fef46c2d60e01b815260040160405180910390fd5b5090565b6040516001600160a01b03831660248201526044810182905261381390849063a9059cbb60e01b90606401613779565b505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060016000838152600660209081526040808320878452909152902060050154600160301b900460ff1660038111156138a4576138a461458b565b146138b157506000613d41565b60006138c062093a80856148db565b905060007f000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221890506000600460008681526020019081526020016000206040518060c00160405290816000820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016001820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160009054906101000a90046001600160a01b03166001600160a01b03166001600160a01b031681526020016002820160149054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff16815260200160028201601a9054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff168152602001600382015481525050905060006006600087815260200190815260200160002060008881526020019081526020016000206040518060e001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820160009054906101000a900465ffffffffffff1665ffffffffffff1665ffffffffffff1681526020016005820160069054906101000a900460ff166003811115613aab57613aab61458b565b6003811115613abc57613abc61458b565b9052509050600260c0820181905250604082810151905163615e523760e01b81526001600160a01b0391821660048201529084169063615e523790602401600060405180830381600087803b158015613b1457600080fd5b505af1158015613b28573d6000803e3d6000fd5b505050506000613b3c836040015186613ede565b905080613b4f5781516080830152613c69565b60008260400151821015613b8557670de0b6b3a7640000836020015183613b769190614858565b613b809190614836565b613b88565b82515b606084018190528351909150613b9f9082906148c4565b60808401526000888152600860205260409081902054905163612d26cf60e11b8152600481018a9052602481018b9052604481018390526001600160a01b0390911690819063c25a4d9e906064016020604051808303816000875af1158015613c0c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c3091906149bf565b613c4d5760405163cc48ad7f60e01b815260040160405180910390fd5b6020850151613c66906001600160a01b031682846137e3565b50505b60008781526006602090815260408083208b84528252918290208451815590840151600182015590830151600282015560608301516003808301919091556080840151600483015560a084015160058301805465ffffffffffff90921665ffffffffffff1983168117825560c0870151879594909366ffffffffffffff191690911790600160301b908490811115613d0357613d0361458b565b02179055505060405189915088907f8deaacba1e0606cd9e75aec4cb0fefc4c1df8d262d3a05f83107f8c067e8891f90600090a36001955050505050505b92915050565b600081815260056020526040812054613d735760405163b31fbda360e01b815260040160405180910390fd5b60008281526005602052604081208054613d8f906001906148c4565b81548110613d9f57613d9f614877565b90600052602060002090600591828204019190066006029054906101000a900465ffffffffffff1665ffffffffffff1690506000613ddb6107d5565b9050808210613e015762093a80613df282846148c4565b613dfc9190614836565b613e04565b60005b949350505050565b6000613e61826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166140959092919063ffffffff16565b8051909150156138135780806020019051810190613e7f91906149bf565b6138135760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161096e565b6040516303f20c7d60e51b81526001600160a01b037f00000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b8116600483015283811660248301526000917f000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221891839190831690637e418fa090604401602060405180830381865afa158015613f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f989190614983565b6040516301e8cff360e31b81526001600160a01b037f00000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b811660048301528781166024830152919250600091841690630f467f9890604401606060405180830381865afa15801561400c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061403091906149e1565b9050848211156140465760009350505050613d41565b8481604001511161405d5760009350505050613d41565b805161406f5760009350505050613d41565b84816040015161407f91906148c4565b815161408b9190614858565b9695505050505050565b60606140a484846000856140ae565b90505b9392505050565b60608247101561410f5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161096e565b6001600160a01b0385163b6141665760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161096e565b600080866001600160a01b031685876040516141829190614a77565b60006040518083038185875af1925050503d80600081146141bf576040519150601f19603f3d011682016040523d82523d6000602084013e6141c4565b606091505b50915091506141d48282866141df565b979650505050505050565b606083156141ee5750816140a7565b8251156141fe5782518084602001fd5b8160405162461bcd60e51b815260040161096e9190614a93565b6040518060e001604052806000815260200160008152602001600081526020016000815260200160008152602001600065ffffffffffff168152602001600060038111156142685761426861458b565b905290565b828054828255906000526020600020906004016005900481019282156143125791602002820160005b838211156142de57835183826101000a81548165ffffffffffff021916908365ffffffffffff1602179055509260200192600601602081600501049283019260010302614296565b80156143105782816101000a81549065ffffffffffff02191690556006016020816005010492830192600103026142de565b505b506137df9291505b808211156137df576000815560010161431a565b60008083601f84011261434057600080fd5b50813567ffffffffffffffff81111561435857600080fd5b6020830191508360208260051b850101111561437357600080fd5b9250929050565b6000806000806040858703121561439057600080fd5b843567ffffffffffffffff808211156143a857600080fd5b6143b48883890161432e565b909650945060208701359150808211156143cd57600080fd5b506143da8782880161432e565b95989497509550505050565b60008060008060008060006080888a03121561440157600080fd5b873567ffffffffffffffff8082111561441957600080fd5b6144258b838c0161432e565b909950975060208a0135965060408a013591508082111561444557600080fd5b6144518b838c0161432e565b909650945060608a013591508082111561446a57600080fd5b506144778a828b0161432e565b989b979a50959850939692959293505050565b600080600080608085870312156144a057600080fd5b5050823594602084013594506040840135936060013592509050565b803565ffffffffffff81168114611f0157600080fd5b600080600080608085870312156144e857600080fd5b843593506144f8602086016144bc565b93969395505050506040820135916060013590565b80356001600160a01b0381168114611f0157600080fd5b6000806040838503121561453757600080fd5b823591506145476020840161450d565b90509250929050565b60006020828403121561456257600080fd5b5035919050565b6000806040838503121561457c57600080fd5b50508035926020909101359150565b634e487b7160e01b600052602160045260246000fd5b600481106145bf57634e487b7160e01b600052602160045260246000fd5b9052565b602080825282518282018190526000919060409081850190868401855b8281101561464c5781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a08082015165ffffffffffff169086015260c09081015190614637818701836145a1565b505060e09390930192908501906001016145e0565b5091979650505050505050565b60006020828403121561466b57600080fd5b6140a78261450d565b600080600080600080600060e0888a03121561468f57600080fd5b6146988861450d565b96506146a66020890161450d565b95506146b4604089016144bc565b969995985095966060810135965060808101359560a0820135955060c0909101359350915050565b600060e08201905088825287602083015286604083015285606083015284608083015265ffffffffffff841660a083015261471a60c08301846145a1565b98975050505050505050565b6000806040838503121561473957600080fd5b6147428361450d565b946020939093013593505050565b60008060006040848603121561476557600080fd5b83359250602084013567ffffffffffffffff81111561478357600080fd5b61478f8682870161432e565b9497909650939450505050565b6020808252825182820181905260009190848201906040850190845b818110156147dc57835165ffffffffffff16835292840192918401916001016147b8565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b818110156147dc57835183529284019291840191600101614804565b634e487b7160e01b600052601160045260246000fd5b60008261485357634e487b7160e01b600052601260045260246000fd5b500490565b600081600019048311821515161561487257614872614820565b500290565b634e487b7160e01b600052603260045260246000fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6000828210156148d6576148d6614820565b500390565b600082198211156148ee576148ee614820565b500190565b600065ffffffffffff80831681851680830382111561491457614914614820565b01949350505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052604160045260246000fd5b600060001982141561497c5761497c614820565b5060010190565b60006020828403121561499557600080fd5b5051919050565b6000602082840312156149ae57600080fd5b815180600f0b81146140a757600080fd5b6000602082840312156149d157600080fd5b815180151581146140a757600080fd5b6000606082840312156149f357600080fd5b6040516060810181811067ffffffffffffffff82111715614a2457634e487b7160e01b600052604160045260246000fd5b80604052508251815260208301516020820152604083015160408201528091505092915050565b60005b83811015614a66578181015183820152602001614a4e565b838111156137b05750506000910152565b60008251614a89818460208701614a4b565b9190910192915050565b6020815260008251806020840152614ab2816040850160208701614a4b565b601f01601f1916919091016040019291505056fea2646970667358221220adf045b10094b82b9dc74fc0d4d0b658123db17c6ff26e57dcc435722b913eed64736f6c634300080a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c124221800000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b0000000000000000000000000482a2d6e2f895125b7237de70c675cd55fe17ca
-----Decoded View---------------
Arg [0] : _gaugeController (address): 0x901c8aA6A61f74aC95E7f397E22A0Ac7c1242218
Arg [1] : _gaugeVoter (address): 0x37aeB332D6E57112f1BFE36923a7ee670Ee9278b
Arg [2] : _chest (address): 0x0482A2d6e2F895125b7237de70c675cd55FE17Ca
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000901c8aa6a61f74ac95e7f397e22a0ac7c1242218
Arg [1] : 00000000000000000000000037aeb332d6e57112f1bfe36923a7ee670ee9278b
Arg [2] : 0000000000000000000000000482a2d6e2f895125b7237de70c675cd55fe17ca
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $1 | 961.5385 | $961.54 |
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.