More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
15752898 | 25 days ago | Contract Creation | 0 S |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
ThenaAdapter
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 1000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { IERC20 } from "openzeppelin/token/ERC20/IERC20.sol"; import { SafeERC20 } from "openzeppelin/token/ERC20/utils/SafeERC20.sol"; import { IRewardsDistributor } from "./interfaces/IRewardsDistributor.sol"; import { IVotingEscrow } from "./interfaces/IVotingEscrow.sol"; import { IVoter } from "./interfaces/IVoter.sol"; import { IMinter } from "./interfaces/IMinter.sol"; import { IAdapter, Adapter } from "src/libraries/Adapter.sol"; import { IRewardDistributor } from "src/interfaces/IRewardDistributor.sol"; import { IPuppeteer } from "src/interfaces/IPuppeteer.sol"; import { BaseAdapter, Marionette } from "src/adapters/BaseAdapter.sol"; import { IERC721Receiver } from "openzeppelin/token/ERC721/IERC721Receiver.sol"; import { IQuestRewardsDistributor } from "./interfaces/IQuestRewardsDistributor.sol"; import { ISwapper } from "./interfaces/ISwapper.sol"; /** * @title ThenaAdapter * @dev Marionette adapter for the Thena protocol. It allows Puppeteer to manage veNFT tokens. * vote, claim and swap rewards, compound voting power, and manage lock duration. */ contract ThenaAdapter is BaseAdapter, IERC721Receiver { using SafeERC20 for IERC20; /** * @param active bool Whether or not the veNFT is active * @param initialEpoch uint256 The initial epoch of the veNFT * @param lastTimestampExtended uint256 The last timestamp the lock was extended * @param outstandingRewardsReceiver address The address of the outstanding rewards receiver * @param rewardMode Marionette.RewardMode The reward mode of the veNFT * @param lockMode Marionette.LockMode The lock mode of the veNFT */ struct VeTokenInfo { bool active; uint256 initialEpoch; uint256 lastTimestampExtended; address outstandingRewardsReceiver; Marionette.RewardMode rewardMode; Marionette.LockMode lockMode; mapping(address => uint256) tokenLastEpochClaimed; mapping(Marionette.RewardMode => mapping(uint256 => uint256)) rewardModeEpochVotePower; } error InvalidInput(); error InvalidLockEnd(); error InvalidBalanceOfToken(); error NotSubscribed(); error NotApprovedOrOwner(); error ZeroAmountReceived(); error NotDistributorOrPuppeteer(); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(uint256 veNftId, Marionette.RewardMode rewardMode, Marionette.LockMode lockMode); event Withdraw(uint256 veNftId); event Subscribe(uint256 veNftId, Marionette.RewardMode rewardMode, Marionette.LockMode lockMode); event Unsubscribe(uint256 veNftId); event Vote(uint256 veNftId, uint256 balanceOfNFT); event SetSwapper(address swapper); event SetGaugesAndWeights(address[] gauges, uint256[] weights); event IncreaseAmount(uint256 veNftId, uint256 amount); event IncreaseUnlockTime(uint256 veNftId, uint256 lockDuration); event InitiateWithdraw(uint256 veNftId, address receiver); event EmergencyWithdraw(address[] tokens, uint256[] amounts, address receiver); /*////////////////////////////////////////////////////////////// ADAPTER STORAGE //////////////////////////////////////////////////////////////*/ IVotingEscrow public constant VE = IVotingEscrow(0x0966CAE7338518961c2d35493D3EB481A75bb86B); IVoter public constant VOTER = IVoter(0xB84194E28f624BBBA3C9181F3a1120eE76469337); address public immutable THE; // Epoch duration uint256 internal constant EPOCH = 1 weeks; uint256 internal constant MAX_TIME = 52 * 7 * 86_400; mapping(uint256 => VeTokenInfo) public veTokenInfo; uint256 public minBalanceOfToken; address[] public gauges; uint256[] public weights; address public swapper; /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ modifier onlySwapperOrPuppeteer() { if (msg.sender != swapper && msg.sender != address(PUPPETEER)) revert NotDistributorOrPuppeteer(); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR AND INITIALIZATION //////////////////////////////////////////////////////////////*/ constructor(IPuppeteer _puppeteer, address initialTHE) BaseAdapter(_puppeteer) { THE = initialTHE; } function getAdapterCalls() public pure override returns (Adapter.Calls memory) { return Adapter.Calls({ beforeTransfer: false, beforeSetDefaultConfig: true, beforeSetCustomConfig: true, beforeSwap: false, beforeClaim: false, beforePause: false }); } /*////////////////////////////////////////////////////////////// ADAPTER FUNCTIONS //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAdapter /// @dev set default gauges and weights, default reward token, swapper, and minimum balance of the veNFT function beforeInitialize( address, Marionette.VoteConfig memory _defaultVoteConfig, Marionette.RewardConfig memory _defaultRewardConfig, Marionette.LockConfig memory, bytes calldata _adapterData ) external override onlyPuppeteer returns (bytes4) { // set default vote config _setGaugesAndWeights(_defaultVoteConfig.gauges, _defaultVoteConfig.weights); // set swapper address if (_defaultRewardConfig.data.length > 0) { (address _swapper) = abi.decode(_defaultRewardConfig.data, (address)); _setSwapper(_swapper); } // set minBalanceOfToken (uint256 _minBalanceOfToken) = abi.decode(_adapterData, (uint256)); minBalanceOfToken = _minBalanceOfToken; // approve VE to THE IERC20(THE).approve(address(VE), type(uint256).max); return IAdapter.beforeInitialize.selector; } /// @inheritdoc IAdapter function beforeTransfer(address _from, address _to, uint256 _tokenId, bytes calldata _adapterData) external override onlyPuppeteer returns (bytes4) { // On mint check reward config to deposit or subscribe if (_from == address(0)) { (Marionette.RewardConfig memory _rewardConfig, Marionette.LockConfig memory _lockConfig) = abi.decode(_adapterData, (Marionette.RewardConfig, Marionette.LockConfig)); if (_rewardConfig.rewardMode == Marionette.RewardMode.VoteOnly) { // If the reward mode is VoteOnly, subscribe veNFT _subscribe(_tokenId, _rewardConfig.rewardMode, _lockConfig.lockMode); } else { // Otherwise, deposit veNFT _deposit(_to, _tokenId, _rewardConfig.rewardMode, _lockConfig.lockMode); } } else if (_to == address(0)) { // On burn, unsubscribe or withdraw veNFT if (veTokenInfo[_tokenId].rewardMode == Marionette.RewardMode.VoteOnly) { _unsubscribe(_tokenId); } else if (_canBurn(_tokenId)) { // If the token can be burned, withdraw it _withdraw(_from, _tokenId); } else { // Otherwise, initiate a withdrawal _initiateWithdrawal(_tokenId, _from); } } return IAdapter.beforeTransfer.selector; } /// @inheritdoc IAdapter function beforeSetDefaultConfig( address, Marionette.ConfigType calldata _configType, bytes calldata _configData, bytes calldata _adapterData ) external override onlyPuppeteer returns (bytes4) { (Marionette.VoteConfig memory _voteConfig, Marionette.RewardConfig memory _rewardConfig,) = abi.decode(_configData, (Marionette.VoteConfig, Marionette.RewardConfig, Marionette.LockConfig)); if (_configType.reward) { // Update swapper address if (_rewardConfig.data.length > 0) { (address _swapper) = abi.decode(_rewardConfig.data, (address)); _setSwapper(_swapper); } } if (_configType.vote) { // Update gauges and weights arrays _setGaugesAndWeights(_voteConfig.gauges, _voteConfig.weights); } // Update minimum balance of token if (_adapterData.length > 0) { (uint256 _minBalanceOfToken) = abi.decode(_adapterData, (uint256)); minBalanceOfToken = _minBalanceOfToken; } return IAdapter.beforeSetDefaultConfig.selector; } /// @inheritdoc IAdapter function beforeSetCustomConfig( address _sender, uint256 _tokenId, Marionette.ConfigType calldata _configType, bytes calldata _configData, bytes calldata ) external override onlyPuppeteer returns (bytes4) { VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; (Marionette.RewardConfig memory _rewardConfig, Marionette.LockConfig memory _lockConfig) = abi.decode(_configData, (Marionette.RewardConfig, Marionette.LockConfig)); if (_rewardConfig.rewardMode == Marionette.RewardMode.Custom) { revert InvalidInput(); } if (_configType.reward) { // if current reward mode is VoteOnly and new reward mode is not VoteOnly, unsubscribe and deposit if ( veInfo.rewardMode == Marionette.RewardMode.VoteOnly && _rewardConfig.rewardMode != Marionette.RewardMode.VoteOnly ) { _unsubscribe(_tokenId); _deposit(_sender, _tokenId, _rewardConfig.rewardMode, veInfo.lockMode); } else if ( !( veInfo.rewardMode == Marionette.RewardMode.Compound && _rewardConfig.rewardMode == Marionette.RewardMode.Default ) && !( veInfo.rewardMode == Marionette.RewardMode.Default && _rewardConfig.rewardMode == Marionette.RewardMode.Compound ) ) { revert InvalidInput(); } } if (_configType.lock) { if (veInfo.lockMode != Marionette.LockMode.Max && _lockConfig.lockMode == Marionette.LockMode.Max) { veInfo.lockMode = _lockConfig.lockMode; _increaseUnlockTime(_tokenId); } else if ( veInfo.lockMode != Marionette.LockMode.Maintain && _lockConfig.lockMode == Marionette.LockMode.Maintain ) { veInfo.lastTimestampExtended = block.timestamp; veInfo.lockMode = _lockConfig.lockMode; } else { veInfo.lockMode = _lockConfig.lockMode; } } if (_configType.vote || (!_configType.lock && !_configType.reward)) { revert InvalidInput(); } return IAdapter.beforeSetCustomConfig.selector; } ///@inheritdoc IAdapter function execute(address, bytes calldata adapterData) external override returns (bytes4) { // Decode the function signature from adapterData (bytes4 functionSignature) = abi.decode(adapterData, (bytes4)); // Call the appropriate function based on the decoded function signature if (functionSignature == this.voteMany.selector) { (, uint256[] memory tokenIds) = abi.decode(adapterData, (bytes4, uint256[])); voteMany(tokenIds); } else if (functionSignature == this.claimQuests.selector) { (, address distributor, address[] memory tokens, IQuestRewardsDistributor.ClaimParams[] memory claimsData) = abi.decode(adapterData, (bytes4, address, address[], IQuestRewardsDistributor.ClaimParams[])); claimQuests(distributor, tokens, claimsData); } else if (functionSignature == this.increaseMany.selector) { (, uint256[] memory _tokenIds, uint256[] memory _amounts, address _depositor, bool pullToken) = abi.decode(adapterData, (bytes4, uint256[], uint256[], address, bool)); increaseMany(_tokenIds, _amounts, _depositor, pullToken); } else if (functionSignature == this.extendMany.selector) { (, uint256[] memory _tokenIds) = abi.decode(adapterData, (bytes4, uint256[])); extendMany(_tokenIds); } else if (functionSignature == this.emergencyWithdraw.selector) { (, address[] memory tokens, uint256[] memory amounts, address receiver) = abi.decode(adapterData, (bytes4, address[], uint256[], address)); emergencyWithdraw(tokens, amounts, receiver); } else { revert InvalidInput(); } return IAdapter.execute.selector; } /** * @dev Vote for multiple tokens. * @param tokenIds An array of token IDs to vote for. */ function voteMany(uint256[] memory tokenIds) public onlyPuppeteer { uint256 tokenIdsLength = tokenIds.length; for (uint256 i; i < tokenIdsLength; i++) { uint256 tokenId = tokenIds[i]; if (veTokenInfo[tokenId].active) { // Check if the contract is approved or owner of the token if (!VE.isVotingApprovedOrOwner(address(this), tokenId)) { // if not unsub _unsubscribe(tokenId); } else { _vote(tokenId); } } } } /** * @dev Claims bribes through rewards distributor and send them to the distributionswapper * @param distributor Address of the rewards distributor * @param tokens Array of tokens to claim * @param _claimsData Claim data for the quest merkle distributor */ function claimQuests( address distributor, address[] memory tokens, IQuestRewardsDistributor.ClaimParams[] memory _claimsData ) public onlyPuppeteer { IQuestRewardsDistributor(distributor).multiClaim(address(this), _claimsData); uint256 length = tokens.length; for (uint256 i; i < length; ++i) { IERC20(tokens[i]).safeTransfer(swapper, _claimsData[i].amount); } } /** * @dev Deposits `_amount` additional tokens for `_tokenId` without modifying the unlock time * @param _tokenIds An array of token IDs to increase the balance of * @param _amounts An array of amounts to increase the balance of each corresponding token ID * @param _depositor The address of the depositor * @param pullToken Whether or not to pull the tokens from the depositor */ function increaseMany(uint256[] memory _tokenIds, uint256[] memory _amounts, address _depositor, bool pullToken) public onlySwapperOrPuppeteer { uint256 tokenIdsLength = _tokenIds.length; if (pullToken) { uint256 totalAmount; // Get the total amount of THE to be increased for (uint256 i; i < tokenIdsLength; i++) { if (!veTokenInfo[_tokenIds[i]].active) revert NotSubscribed(); totalAmount += _amounts[i]; } // Transfer the total amount of THE to the contract IERC20(THE).safeTransferFrom(_depositor, address(this), totalAmount); } for (uint256 i; i < tokenIdsLength; i++) { _increaseAmount(_tokenIds[i], _amounts[i]); } } /** * @dev Extends the lock duration of multiple tokens to match the duration the user choose to maintain * Only tokens with a lock mode of `Maintain` will have their duration increased * @param _tokenIds An array of token IDs to extend the lock duration for */ function extendMany(uint256[] memory _tokenIds) public onlyPuppeteer { uint256 tokenIdsLength = _tokenIds.length; // Loop through each token ID and extend the lock duration if the lock mode is Maintain for (uint256 i; i < tokenIdsLength; i++) { if ( ( veTokenInfo[_tokenIds[i]].lockMode == Marionette.LockMode.Maintain || veTokenInfo[_tokenIds[i]].lockMode == Marionette.LockMode.Max ) && veTokenInfo[_tokenIds[i]].active ) { _increaseUnlockTime(_tokenIds[i]); } } } /** * @dev Emergency withdraw of tokens */ function emergencyWithdraw(address[] memory tokens, uint256[] memory amounts, address receiver) public onlyPuppeteer { uint256 tokensLength = tokens.length; for (uint256 i; i < tokensLength; i++) { IERC20(tokens[i]).safeTransfer(receiver, amounts[i]); } emit EmergencyWithdraw(tokens, amounts, receiver); } /*////////////////////////////////////////////////////////////// VIEW FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @dev Return whether or not a token can be burned * @param _tokenId The ID of the veNFT */ function canBurn(uint256 _tokenId) public view returns (bool) { return _canBurn(_tokenId); } /*////////////////////////////////////////////////////////////// ERC721 RECEIVER //////////////////////////////////////////////////////////////*/ function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } /*////////////////////////////////////////////////////////////// INTERNAL FUNCTIONS //////////////////////////////////////////////////////////////*/ /** * @dev Deposit `_amount` additional tokens for `_tokenId` without modifying the unlock time * @param _tokenId ID of the token to increase the amount of * @param _amount Amount of tokens to deposit and add to the lock */ function _increaseAmount(uint256 _tokenId, uint256 _amount) internal { VE.increase_amount(_tokenId, _amount); emit IncreaseAmount(_tokenId, _amount); } /** * @dev Increases the unlock time of a token by a specified duration. * @param _tokenId The ID of the token to increase the unlock time for. */ function _increaseUnlockTime(uint256 _tokenId) internal { VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; IVotingEscrow.LockedBalance memory lockedBalance = VE.locked(_tokenId); Marionette.LockMode lm = veInfo.lockMode; uint256 lastTimestampExtended = veInfo.lastTimestampExtended; uint256 lastExtendedEpoch = lastTimestampExtended / EPOCH; uint256 currentEpoch = block.timestamp / EPOCH; if (lastExtendedEpoch <= currentEpoch) { uint256 targetUnlockTime = (block.timestamp + MAX_TIME) / EPOCH * EPOCH; if (lm == Marionette.LockMode.Max && lockedBalance.end < targetUnlockTime) { uint256 lockDuration = targetUnlockTime - block.timestamp; VE.increase_unlock_time(_tokenId, lockDuration); emit IncreaseUnlockTime(_tokenId, lockDuration); } else if (lm == Marionette.LockMode.Maintain) { uint256 lockDuration = (lockedBalance.end - lastTimestampExtended) / EPOCH * EPOCH + EPOCH; if (lockDuration > MAX_TIME) { if (lockedBalance.end < targetUnlockTime) { lockDuration = targetUnlockTime - block.timestamp; } else { return; } } veInfo.lastTimestampExtended = block.timestamp; VE.increase_unlock_time(_tokenId, lockDuration); emit IncreaseUnlockTime(_tokenId, lockDuration); } } } /** * @dev Vote using through VOTER.vote() * only votes is the user have not veted this epoch and the balance is greater than `minBalanceOfToken` * @param _tokenId Id of veNFT you are voting with. */ function _vote(uint256 _tokenId) internal { uint256 balanceOfNFT = VE.balanceOfNFT(_tokenId); if (balanceOfNFT >= minBalanceOfToken) { VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; Marionette.RewardMode rm = veInfo.rewardMode; // if reward mode is not VoteOnly, update the total vote power for the active epoch // used to compute reward distribution if (rm != Marionette.RewardMode.VoteOnly) { uint256 activePeriod = _currentEpoch(); veInfo.rewardModeEpochVotePower[rm][activePeriod] = balanceOfNFT; // if the veNFT has voted for the active epoch using another reward mode config // reset this vote power for the active epoch if (rm == Marionette.RewardMode.Compound) { uint256 defaultVotePower = veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Default][activePeriod]; if (defaultVotePower > 0) { veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Default][activePeriod] = 0; } } if (rm == Marionette.RewardMode.Default) { uint256 compoundVotePower = veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Compound][activePeriod]; if (compoundVotePower > 0) { veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Compound][activePeriod] = 0; } } } VOTER.vote(_tokenId, gauges, weights); emit Vote(_tokenId, balanceOfNFT); } } /** * @dev Deactivate veNFT and transfer it to the owner * @param _tokenId The ID of the veNFT * @param _owner The address of the veNFT owner */ function _initiateWithdrawal(uint256 _tokenId, address _owner) internal { VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; veInfo.active = false; veInfo.outstandingRewardsReceiver = _owner; uint256 activePeriod = _currentEpoch(); Marionette.RewardMode rm = veInfo.rewardMode; // clears vote power for the active epoch uint256 currVotePower = veInfo.rewardModeEpochVotePower[rm][activePeriod]; if (currVotePower > 0) { veInfo.rewardModeEpochVotePower[rm][activePeriod] = 0; } // reset current vote to allow transfer VOTER.reset(_tokenId); // Transfer the veNFT back to owner VE.safeTransferFrom(address(this), _owner, _tokenId, ""); emit InitiateWithdraw(_tokenId, _owner); } /** * @dev set swapper address */ function _setSwapper(address _swapper) internal { swapper = _swapper; emit SetSwapper(_swapper); } /** * @dev require `_address` is approved to manage veNFT * @param _address address to check * @param _tokenId veNFT id */ function _isApprovedOrOwner(address _address, uint256 _tokenId) internal { if (!VE.isVotingApprovedOrOwner(_address, _tokenId)) revert NotApprovedOrOwner(); } /** * @dev require ve position has enough lock duration left * @param _tokenId veNFT id */ function _isAuthorizedBalanceAndDuration(uint256 _tokenId) internal view { IVotingEscrow.LockedBalance memory lockedBalance = VE.locked(_tokenId); if (lockedBalance.end <= (block.timestamp + EPOCH)) revert InvalidLockEnd(); if (uint256(int256(lockedBalance.amount)) < minBalanceOfToken) revert InvalidBalanceOfToken(); } /** * @dev get current epoch */ function _currentEpoch() internal view returns (uint256) { return VOTER.currentPeriod(); } /** * @dev Transfers veNFT this contract and record the user preferences * @param _owner The owner of the veNFT tokens * @param _tokenId The ID of the veNFT token being deposited * @param _rewardMode The reward mode selected by the subscriber * @param _lockMode The lock mode selected by the subscriber */ function _deposit( address _owner, uint256 _tokenId, Marionette.RewardMode _rewardMode, Marionette.LockMode _lockMode ) internal { // check lock duration and balance requirements _isAuthorizedBalanceAndDuration(_tokenId); if (_rewardMode == Marionette.RewardMode.Custom) { revert InvalidInput(); } if (_lockMode == Marionette.LockMode.Max) { _increaseUnlockTime(_tokenId); } // reset current vote to allow transfer if (VE.voted(_tokenId)) VOTER.reset(_tokenId); VE.safeTransferFrom(_owner, address(this), _tokenId, ""); VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; // record subscriber veNFT preferences veInfo.active = true; veInfo.rewardMode = _rewardMode; veInfo.lockMode = _lockMode; veInfo.lastTimestampExtended = block.timestamp; veInfo.outstandingRewardsReceiver = address(0); if (veInfo.initialEpoch == 0) { veInfo.initialEpoch = _currentEpoch(); } emit Deposit(_tokenId, _rewardMode, _lockMode); } /** * @dev Transfers the veNFT back to marionette owner * @param _owner The address of the owner of the veNFT token * @param _tokenId The ID of the veNFT token to be withdrawn */ function _withdraw(address _owner, uint256 _tokenId) internal { if (veTokenInfo[_tokenId].active) { // reset current vote to allow transfer VOTER.reset(_tokenId); // Transfer the veNFT back to owner VE.safeTransferFrom(address(this), _owner, _tokenId, ""); } delete veTokenInfo[_tokenId]; emit Withdraw(_tokenId); } /** * @dev subscribe a veNFT by recording user preferences * @param _tokenId The ID of the veNFT to be subscriber * @param _rewardMode The reward mode for the subscriber * @param _lockMode The lock mode for the subscriber. */ function _subscribe(uint256 _tokenId, Marionette.RewardMode _rewardMode, Marionette.LockMode _lockMode) internal { // check if sender is approved to manage veNFT _isApprovedOrOwner(address(this), _tokenId); // check lock duration and balance requirements _isAuthorizedBalanceAndDuration(_tokenId); if (_rewardMode == Marionette.RewardMode.Custom) { revert InvalidInput(); } if (_lockMode == Marionette.LockMode.Max) { _increaseUnlockTime(_tokenId); } VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; veInfo.active = true; veInfo.rewardMode = _rewardMode; veInfo.lockMode = _lockMode; veInfo.initialEpoch = _currentEpoch(); veInfo.lastTimestampExtended = block.timestamp; emit Subscribe(_tokenId, _rewardMode, _lockMode); } /** * @dev Unsubscribes a veNFT and claims rewards if any * @param _tokenId The ID of the veNFT */ function _unsubscribe(uint256 _tokenId) internal { // delete veNFT preferences delete veTokenInfo[_tokenId]; emit Unsubscribe(_tokenId); } /** * @dev Sets the gauges and weights for the adapter. * @param _gauges An array of gauge addresses. * @param _weights An array of gauge weights. */ function _setGaugesAndWeights(address[] memory _gauges, uint256[] memory _weights) internal { // Check that the length of `_gauges` and `_weights` are the same if (_gauges.length != _weights.length) revert InvalidInput(); gauges = _gauges; weights = _weights; emit SetGaugesAndWeights(_gauges, _weights); } /** * @dev Return whether or not a token can be burned * @param _tokenId The ID of the veNFT */ function _canBurn(uint256 _tokenId) internal view returns (bool) { uint256 lastEpoch = _currentEpoch() - 1; VeTokenInfo storage veInfo = veTokenInfo[_tokenId]; uint256 tokenVotesLastEpoch = veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Compound][lastEpoch] + veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Default][lastEpoch]; uint256 tokenVotesEpochMinus2 = veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Compound][lastEpoch - 1] + veInfo.rewardModeEpochVotePower[Marionette.RewardMode.Default][lastEpoch - 1]; // if token is vote only return true // if token has voted last epoch return false (cant claim) // if token has not voted voted 2 epochs ago // or rewards from two epoch ago have been claimed return true bool isVoteOnly = veInfo.rewardMode == Marionette.RewardMode.VoteOnly; bool hasVotedLastEpoch = tokenVotesLastEpoch > 0; bool hasNotVotedEpochMinus2 = tokenVotesEpochMinus2 == 0; return isVoteOnly || (!hasVotedLastEpoch && hasNotVotedEpochMinus2); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) 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 a `value` amount of tokens 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 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @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); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
//SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; /** * @title IRewardsDistributor * @author 0xMemoryGrinder * @dev MerkleDistributor interface which updates the merkle root when the claims are made */ interface IRewardsDistributor { struct ClaimParams { address token; uint256 epoch; uint256 amount; bytes32[] merkleProof; } struct ReclaimParams { address account; ClaimParams[] params; } /** * @dev Claim a token reward for a given account * @param epoch Epoch to claim rewards for * @param token Token address to claim * @param account Account to claim rewards for * @param amount Rewards amount to claim * @param merkleProof Merkle proof to validate the claim */ function claim( uint256 epoch, address token, address account, uint256 amount, bytes32[] calldata merkleProof ) external; /** * @dev Claim multiple token rewards for given accounts * @param account Account to claim rewards for * @param params ClaimParams array containing the tokens, amounts and merkle proofs */ function multiClaim(address account, ClaimParams[] calldata params) external; /** * @dev Update the merkle root * @param epoch Epoch id * @param newMerkleRoot New merkle root after a harvest, set by the operator */ function addEpochRewards(uint256 epoch, bytes32 newMerkleRoot) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVotingEscrow { struct LockedBalance { int128 amount; uint256 end; } function ownerOf(uint256 tokenId) external view returns (address); function approve(address _spender, uint256 _tokenId) external; function isVotingApprovedOrOwner(address _spender, uint256 _tokenId) external returns (bool); function locked(uint256 _tokenId) external view returns (LockedBalance memory); function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; function increase_amount(uint256 _tokenId, uint256 _value) external; function increase_unlock_time(uint256 _tokenId, uint256 _lockDuration) external; function balanceOfNFT(uint256 _tokenId) external view returns (uint256); function attachments(uint256 _tokenId) external view returns (uint256); function voted(uint256 _tokenId) external view returns (bool); function create_lock(uint _value, uint _lock_duration) external returns (uint); function approveVoting(address _approved, uint256 _tokenId) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IVoter { function maxVotingNum() external view returns (uint256); function lastVoted(uint256 tokenId) external view returns (uint256); function vote(uint256 _tokenId, address[] calldata _poolVote, uint256[] calldata _weights) external; function reset(uint256 _tokenId) external; function claimBribes(address[] memory _bribes, address[][] memory _tokens) external; function claimFees(address[] memory _fees, address[][] memory _tokens) external; function internal_bribes(address _gauge) external view returns (address); function external_bribes(address _gauge) external view returns (address); function gauges(address _pair) external view returns (address); function getGaugeVotes(address _pool) external view returns (uint256); function poolVote(uint256 _tokenId, uint256 i) external view returns (address); function currentPeriod() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IMinter { function active_period() external view returns (uint256); function update_period() external returns (uint256); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.24; import { IAdapter } from "../interfaces/IAdapter.sol"; /// @notice The protocol decides whether to invoke specific adapter by inspecting the leading bits of the address that /// the adapter contract is deployed to. /// For example, a adapter contract deployed to address: 0x9000000000000000000000000000000000000000 /// has leading bits "1001" which would cause the "before initialize" and "after transfer" adapter to be used. library Adapter { uint256 internal constant BEFORE_TRANSFER_FLAG = 1 << 159; uint256 internal constant BEFORE_SET_DEFAULT_FLAG = 1 << 158; uint256 internal constant BEFORE_SET_CUSTOM_FLAG = 1 << 157; uint256 internal constant BEFORE_SWAP_FLAG = 1 << 156; uint256 internal constant BEFORE_CLAIM_FLAG = 1 << 155; uint256 internal constant BEFORE_PAUSE_FLAG = 1 << 154; struct Calls { bool beforeTransfer; bool beforeSetDefaultConfig; bool beforeSetCustomConfig; bool beforeSwap; bool beforeClaim; bool beforePause; } /// @notice Thrown if the address will not lead to the specified adapter calls being called /// @param adapter The address of the adapter contract error AdapterAddressNotValid(address adapter); /// @notice Adapter did not return its selector error InvalidAdapterResponse(); /// @notice Utility function intended to be used in adapter constructors to ensure /// the deployed adapter address causes the intended adapter to be called /// @param calls The adapter that are intended to be called /// @dev calls param is memory as the function will be called from constructors function validateAdapterAddress(IAdapter self, Calls memory calls) internal pure { if ( calls.beforeTransfer != shouldCallBeforeTransfer(self) || calls.beforeSetDefaultConfig != shouldCallBeforeSetDefaultConfig(self) || calls.beforeSetCustomConfig != shouldCallBeforeSetCustomConfig(self) || calls.beforeSwap != shouldCallBeforeSwap(self) || calls.beforeClaim != shouldCallBeforeClaim(self) || calls.beforePause != shouldCallBeforePause(self) ) { revert AdapterAddressNotValid(address(self)); } } /// @notice Ensures that the adapter address includes at least one adapter flag or dynamic fees, or is the 0 address /// @param adapter The adapter to verify function isValidAdapterAddress(IAdapter adapter) internal pure returns (bool) { return uint160(address(adapter)) >= BEFORE_PAUSE_FLAG; } function shouldCallBeforeTransfer(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_TRANSFER_FLAG != 0; } function shouldCallBeforeSetDefaultConfig(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_SET_DEFAULT_FLAG != 0; } function shouldCallBeforeSetCustomConfig(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_SET_CUSTOM_FLAG != 0; } function shouldCallBeforeSwap(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_SWAP_FLAG != 0; } function shouldCallBeforePause(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_PAUSE_FLAG != 0; } function shouldCallBeforeClaim(IAdapter self) internal pure returns (bool) { return uint256(uint160(address(self))) & BEFORE_CLAIM_FLAG != 0; } }
//SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; /** * @title IRewardsDistributor * @author 0xMemoryGrinder * @dev MerkleDistributor interface which updates the merkle root when the claims are made */ interface IRewardDistributor { struct ClaimParams { address token; uint256 epoch; uint256 index; uint256 amount; bytes32[] merkleProof; } /** * @dev Claim a token reward for a given account * @param epoch Epoch to claim rewards for * @param index Index in the merkle tree to claim rewards for * @param token Token address to claim * @param account Account to claim rewards for * @param amount Rewards amount to claim * @param merkleProof Merkle proof to validate the claim */ function claim( uint256 epoch, uint256 index, address token, address account, uint256 amount, bytes32[] calldata merkleProof ) external; /** * @dev Claim multiple token rewards for given accounts * @param account Account to claim rewards for * @param params ClaimParams array containing the tokens, amounts and merkle proofs */ function multiClaim(address account, ClaimParams[] calldata params) external; /** * @dev Update the merkle root * @param epoch Epoch id * @param newMerkleRoot New merkle root after a harvest, set by the operator */ function addEpochRewards(uint256 epoch, bytes32 newMerkleRoot) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { MarionetteKey } from "../types/MarionetteKey.sol"; import { MarionetteId } from "../types/MarionetteId.sol"; import { Marionette } from "../libraries/Marionette.sol"; import { Incentive, IncentiveClaim } from "../types/Incentive.sol"; /** * @title IPuppeteer * @dev Interface for the Puppeteer contract. */ interface IPuppeteer { event Initialize( MarionetteKey indexed key, MarionetteId indexed id, string uri, address indexed admin, Marionette.VoteConfig voteConfig, Marionette.RewardConfig rewardConfig, Marionette.LockConfig lockConfig, bytes adapterData ); event Mint( MarionetteId indexed id, uint256 indexed tokenId, uint256 indexed veId, address recipient, Marionette.RewardConfig rewardConfig, Marionette.LockConfig lockConfig, bytes adapterData ); event Burn(MarionetteId indexed id, uint256 indexed tokenId, bytes adapterData); event SetCustomConfig(MarionetteId indexed id, uint256 tokenId, Marionette.ConfigType configType, bytes configData); event SetDefaultConfig(MarionetteId indexed id, Marionette.ConfigType configType, bytes configData); event SetPaused(MarionetteId indexed id, bool paused); event SetUri(MarionetteId indexed id, string uri); event Execute(MarionetteId indexed id, bytes adapterData); event Claim(MarionetteId indexed id, uint256 indexed tokenId, uint256 indexed veId, bytes adapterData); event Swap(MarionetteId indexed id, bytes adapterData); /** * @dev Initializes a new Marionette * @param key Marionette key * @param uri Marionette URI * @param defaultVoteConfig The default vote configuration * @param defaultRewardConfig The default reward configuration * @param defaultLockConfig The default lock configuration * @param adapterData Additional data to be used by the adapter contract */ function initialize( MarionetteKey memory key, string calldata uri, Marionette.VoteConfig calldata defaultVoteConfig, Marionette.RewardConfig calldata defaultRewardConfig, Marionette.LockConfig calldata defaultLockConfig, bytes calldata adapterData ) external; /** * @dev Mints a new Marionette token and performs any additional adapter-specific actions * @param key Marionette key * @param veId veNFT ID in the voting escrow contract * @param rewardConfig Custom reward configuration * @param lockConfig Custom lock configuration * @param adapterData Additional data to be used by the adapter contract */ function mint( MarionetteKey memory key, uint256 veId, Marionette.RewardConfig calldata rewardConfig, Marionette.LockConfig calldata lockConfig, bytes calldata adapterData ) external; /** * @dev Burns a Marionette NFT and performs any additional adapter-specific actions * @param key Marionette Key * @param tokenId The ID of the Marionette NFT to burn * @param adapterData Additional data to be used by the adapter contract */ function burn(MarionetteKey memory key, uint256 tokenId, bytes calldata adapterData) external; /** * @dev Sets the default configuration for a Marionette instance. * @param key Marionette key * @param configType The type of configuration to set * @param configData The configuration data * @param adapterData Additional data to be used by the adapter contract */ function setDefaultConfig( MarionetteKey memory key, Marionette.ConfigType calldata configType, bytes calldata configData, bytes calldata adapterData ) external; /** * @dev Sets the custom configuration for a Marionette * @param key Marionette key * @param tokenId The ID of the Marionette token * @param configType The type of configuration to set * @param configData The configuration data * @param adapterData Additional data to be used by the adapter contract */ function setCustomConfig( MarionetteKey memory key, uint256 tokenId, Marionette.ConfigType calldata configType, bytes calldata configData, bytes calldata adapterData ) external; /** * @dev Executes a function on the Adapter contract using the provided key and adapter data * @param key Marionette key * @param adapterData Additional data to be used by the adapter contract */ function execute(MarionetteKey memory key, bytes calldata adapterData) external; /** * @dev Sets Marionette paused state * @param key Marionette key * @param paused new paused state * @param adapterData Additional data to be used by the ISwapper contract */ function setPaused(MarionetteKey memory key, bool paused, bytes calldata adapterData) external; /** * @dev Sets the URI for a given Marionette * @param key Marionette key * @param uri new URI */ function setUri(MarionetteKey memory key, string calldata uri) external; /** * @dev Returns the current state of the Marionette * @param tokenId The ID of the Marionette to get the state of * @return A struct containing the current state of the Marionette */ function getMarionette(uint256 tokenId) external view returns (Marionette.MarionetteState memory); /** * @dev Returns the current state of the Marionette key * @param key Marionette key * @return initialized Whether the key has been initialized * @return paused Whether the key is currently paused * @return uri The URI associated with the key * @return defaultVoteConfig The default vote configuration for the key * @return defaultRewardConfig The default reward configuration for the key * @return defaultLockConfig The default lock configuration for the key */ function getState(MarionetteKey memory key) external view returns ( bool initialized, bool paused, string memory uri, Marionette.VoteConfig memory defaultVoteConfig, Marionette.RewardConfig memory defaultRewardConfig, Marionette.LockConfig memory defaultLockConfig ); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { IPuppeteer } from "../interfaces/IPuppeteer.sol"; import { IAdapter } from "../interfaces/IAdapter.sol"; import { Adapter } from "../libraries/Adapter.sol"; import { MarionetteKey } from "../types/MarionetteKey.sol"; import { Marionette } from "../libraries/Marionette.sol"; abstract contract BaseAdapter is IAdapter { error NotPuppeteer(); error NotSelf(); error InvalidMarionette(); error AdapterNotImplemented(); /// @notice The address of the puppeteer IPuppeteer public immutable PUPPETEER; constructor(IPuppeteer _puppeteer) { PUPPETEER = _puppeteer; validateAdapterAddress(this); } /// @dev Only the puppeteer may call this function modifier onlyPuppeteer() { if (msg.sender != address(PUPPETEER)) revert NotPuppeteer(); _; } /// @dev Only marionettes with adapter set to this contract may call this function modifier onlyValidMarionettes(IAdapter adapter) { if (adapter != this) revert InvalidMarionette(); _; } function getAdapterCalls() public pure virtual returns (Adapter.Calls memory); // this function is virtual so that we can override it during testing, // which allows us to deploy an implementation to any address // and then etch the bytecode into the correct address function validateAdapterAddress(BaseAdapter _this) internal virtual { Adapter.validateAdapterAddress(_this, getAdapterCalls()); } function beforeInitialize( address, Marionette.VoteConfig memory, Marionette.RewardConfig memory, Marionette.LockConfig memory, bytes calldata ) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforeTransfer(address, address, uint256, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforeSetDefaultConfig(address, Marionette.ConfigType calldata, bytes calldata, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforeSetCustomConfig(address, uint256, Marionette.ConfigType calldata, bytes calldata, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforeSwap(address, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforeClaim(address, uint256, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function beforePause(address, bool, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } function execute(address, MarionetteKey calldata, bytes calldata) external virtual returns (bytes4) { revert AdapterNotImplemented(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
//SPDX-License-Identifier: MIT pragma solidity 0.8.24; interface IQuestRewardsDistributor { // Events /** * @notice Event emitted when a 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); /** * @notice Event emitted when the Loot Creator address is updated */ event LootCreatorUpdated(address indexed oldCreator, address indexed newCreator); // Functions /** * @notice Checks if the rewards were claimed for a user on a given period * @dev Checks if the rewards were claimed for a 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) external view returns (bool); //Basic Claim /** * @notice Claims the reward for a user for a given period of a Quest * @dev Claims the reward for a 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 ) external; //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; //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; /** * @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); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; interface ISwapper { /** * @notice swap tokens according to the given adapter data * @param tokens Tokens to swap * @param callDatas Aggregator data for the swap */ function swap(address[] calldata tokens, bytes[] calldata callDatas) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { Marionette } from "../libraries/Marionette.sol"; interface IAdapter { function beforeInitialize( address sender, Marionette.VoteConfig memory defaultVoteConfig, Marionette.RewardConfig memory defaultRewardConfig, Marionette.LockConfig memory defaultLockConfig, bytes calldata adapterData ) external returns (bytes4); function beforeTransfer(address from, address to, uint256 veId, bytes calldata adapterData) external returns (bytes4); function beforeSetDefaultConfig( address sender, Marionette.ConfigType calldata configType, bytes calldata configData, bytes calldata adapterData ) external returns (bytes4); function beforeSetCustomConfig( address sender, uint256 tokenId, Marionette.ConfigType calldata configType, bytes calldata configData, bytes calldata adapterData ) external returns (bytes4); function beforeSwap(address sender, bytes calldata adapterData) external returns (bytes4); function beforeClaim(address sender, uint256 veId, bytes calldata adapterData) external returns (bytes4); function beforePause(address sender, bool paused, bytes calldata adapterData) external returns (bytes4); function execute(address sender, bytes calldata adapterData) external returns (bytes4); function increaseMany(uint256[] memory veNFTs, uint256[] memory amounts, address depositor, bool pullToken) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { IAdapter } from "../interfaces/IAdapter.sol"; import { IRewardDistributor } from "../interfaces/IRewardDistributor.sol"; struct Protocol { string protocol; address veAddress; uint24 version; } struct MarionetteKey { Protocol protocol; IAdapter adapter; IRewardDistributor rewardDistributor; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; import { MarionetteKey } from "./MarionetteKey.sol"; type MarionetteId is bytes32; library MarionetteIdLib { function toId(MarionetteKey memory marionetteKey) internal pure returns (MarionetteId) { return MarionetteId.wrap(keccak256(abi.encode(marionetteKey))); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; library Marionette { error InvalidStatus(); struct ConfigType { bool vote; bool reward; bool lock; } enum RewardMode { VoteOnly, Default, Compound, Custom } struct RewardConfig { RewardMode rewardMode; bytes data; } struct VoteConfig { address[] gauges; uint256[] weights; bytes data; } enum LockMode { None, Max, Maintain, Custom } struct LockConfig { LockMode lockMode; bytes data; } struct MarionetteState { uint256 veId; RewardConfig customRewardConfig; LockConfig customLockConfig; } struct State { bool initialized; bool paused; string uri; VoteConfig defaultVoteConfig; RewardConfig defaultRewardConfig; LockConfig defaultLockConfig; mapping(uint256 => MarionetteState) marionette; mapping(uint256 veId => uint256 tokenId) veIdToTokenId; } function initialize( State storage state, string memory uri, VoteConfig memory defaultVoteConfig, RewardConfig memory defaultRewardConfig, LockConfig memory defaultLockConfig ) internal { state.initialized = true; state.paused = true; state.uri = uri; state.defaultVoteConfig = defaultVoteConfig; state.defaultRewardConfig = defaultRewardConfig; state.defaultLockConfig = defaultLockConfig; } function mint( State storage state, uint256 tokenId, uint256 veId, RewardConfig memory rewardConfig, LockConfig memory lockConfig ) internal { state.marionette[tokenId].veId = veId; state.marionette[tokenId].customRewardConfig = rewardConfig; state.marionette[tokenId].customLockConfig = lockConfig; state.veIdToTokenId[veId] = tokenId; } function burn(State storage state, uint256 tokenId) internal { delete state.marionette[tokenId]; } function setPaused(State storage state, bool paused) internal { if (state.paused == paused) revert InvalidStatus(); state.paused = paused; } function setUri(State storage state, string memory uri) internal { state.uri = uri; } function setDefaultConfig(State storage state, ConfigType memory configType, bytes memory configData) internal { if (configType.vote) { (VoteConfig memory config,,) = abi.decode(configData, (VoteConfig, RewardConfig, LockConfig)); state.defaultVoteConfig = config; } if (configType.reward) { (, RewardConfig memory config,) = abi.decode(configData, (VoteConfig, RewardConfig, LockConfig)); state.defaultRewardConfig = config; } if (configType.lock) { (,, LockConfig memory config) = abi.decode(configData, (VoteConfig, RewardConfig, LockConfig)); state.defaultLockConfig = config; } } function setCustomConfig( State storage state, uint256 tokenId, ConfigType memory configType, bytes memory configData ) internal { if (configType.reward) { (RewardConfig memory config,) = abi.decode(configData, (RewardConfig, LockConfig)); state.marionette[tokenId].customRewardConfig = config; } if (configType.lock) { (, LockConfig memory config) = abi.decode(configData, (RewardConfig, LockConfig)); state.marionette[tokenId].customLockConfig = config; } } function getMarionette(State storage state, uint256 tokenId) internal view returns (MarionetteState memory) { return state.marionette[tokenId]; } function getState(State storage state) internal view returns ( bool initialized, bool paused, string memory uri, VoteConfig memory defaultVoteConfig, RewardConfig memory defaultRewardConfig, LockConfig memory defaultLockConfig ) { return ( state.initialized, state.paused, state.uri, state.defaultVoteConfig, state.defaultRewardConfig, state.defaultLockConfig ); } function getTokenId(State storage state, uint256 veId) internal view returns (uint256 tokenId) { return state.veIdToTokenId[veId]; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.24; struct Incentive { address token; bytes32 merkleRoot; uint256 activeAt; } struct IncentiveClaim { address token; address account; uint256 tokenId; uint256 amount; bytes32[] merkleProof; }
{ "remappings": [ "ds-test/=lib/solmate/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "openzeppelin/=lib/openzeppelin-contracts/contracts/", "solmate/=lib/solmate/src/", "solady/=lib/solady/src/", "src/=src/", "test/=test/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts/=lib/openzeppelin-contracts/" ], "optimizer": { "enabled": true, "runs": 1000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IPuppeteer","name":"_puppeteer","type":"address"},{"internalType":"address","name":"initialTHE","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"adapter","type":"address"}],"name":"AdapterAddressNotValid","type":"error"},{"inputs":[],"name":"AdapterNotImplemented","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidBalanceOfToken","type":"error"},{"inputs":[],"name":"InvalidInput","type":"error"},{"inputs":[],"name":"InvalidLockEnd","type":"error"},{"inputs":[],"name":"InvalidMarionette","type":"error"},{"inputs":[],"name":"NotApprovedOrOwner","type":"error"},{"inputs":[],"name":"NotDistributorOrPuppeteer","type":"error"},{"inputs":[],"name":"NotPuppeteer","type":"error"},{"inputs":[],"name":"NotSelf","type":"error"},{"inputs":[],"name":"NotSubscribed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZeroAmountReceived","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"enum Marionette.RewardMode","name":"rewardMode","type":"uint8"},{"indexed":false,"internalType":"enum Marionette.LockMode","name":"lockMode","type":"uint8"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"EmergencyWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncreaseAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockDuration","type":"uint256"}],"name":"IncreaseUnlockTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"InitiateWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"gauges","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"SetGaugesAndWeights","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapper","type":"address"}],"name":"SetSwapper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"enum Marionette.RewardMode","name":"rewardMode","type":"uint8"},{"indexed":false,"internalType":"enum Marionette.LockMode","name":"lockMode","type":"uint8"}],"name":"Subscribe","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"}],"name":"Unsubscribe","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"balanceOfNFT","type":"uint256"}],"name":"Vote","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"veNftId","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"PUPPETEER","outputs":[{"internalType":"contract IPuppeteer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"THE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VE","outputs":[{"internalType":"contract IVotingEscrow","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VOTER","outputs":[{"internalType":"contract IVoter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"beforeClaim","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"components":[{"internalType":"address[]","name":"gauges","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Marionette.VoteConfig","name":"_defaultVoteConfig","type":"tuple"},{"components":[{"internalType":"enum Marionette.RewardMode","name":"rewardMode","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Marionette.RewardConfig","name":"_defaultRewardConfig","type":"tuple"},{"components":[{"internalType":"enum Marionette.LockMode","name":"lockMode","type":"uint8"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct Marionette.LockConfig","name":"","type":"tuple"},{"internalType":"bytes","name":"_adapterData","type":"bytes"}],"name":"beforeInitialize","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"beforePause","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sender","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"components":[{"internalType":"bool","name":"vote","type":"bool"},{"internalType":"bool","name":"reward","type":"bool"},{"internalType":"bool","name":"lock","type":"bool"}],"internalType":"struct Marionette.ConfigType","name":"_configType","type":"tuple"},{"internalType":"bytes","name":"_configData","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"beforeSetCustomConfig","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"components":[{"internalType":"bool","name":"vote","type":"bool"},{"internalType":"bool","name":"reward","type":"bool"},{"internalType":"bool","name":"lock","type":"bool"}],"internalType":"struct Marionette.ConfigType","name":"_configType","type":"tuple"},{"internalType":"bytes","name":"_configData","type":"bytes"},{"internalType":"bytes","name":"_adapterData","type":"bytes"}],"name":"beforeSetDefaultConfig","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"beforeSwap","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"bytes","name":"_adapterData","type":"bytes"}],"name":"beforeTransfer","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"canBurn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"distributor","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"uint256","name":"questID","type":"uint256"},{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct IQuestRewardsDistributor.ClaimParams[]","name":"_claimsData","type":"tuple[]"}],"name":"claimQuests","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"emergencyWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"components":[{"components":[{"internalType":"string","name":"protocol","type":"string"},{"internalType":"address","name":"veAddress","type":"address"},{"internalType":"uint24","name":"version","type":"uint24"}],"internalType":"struct Protocol","name":"protocol","type":"tuple"},{"internalType":"contract IAdapter","name":"adapter","type":"address"},{"internalType":"contract IRewardDistributor","name":"rewardDistributor","type":"address"}],"internalType":"struct MarionetteKey","name":"","type":"tuple"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"adapterData","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"extendMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"gauges","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAdapterCalls","outputs":[{"components":[{"internalType":"bool","name":"beforeTransfer","type":"bool"},{"internalType":"bool","name":"beforeSetDefaultConfig","type":"bool"},{"internalType":"bool","name":"beforeSetCustomConfig","type":"bool"},{"internalType":"bool","name":"beforeSwap","type":"bool"},{"internalType":"bool","name":"beforeClaim","type":"bool"},{"internalType":"bool","name":"beforePause","type":"bool"}],"internalType":"struct Adapter.Calls","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"bool","name":"pullToken","type":"bool"}],"name":"increaseMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"minBalanceOfToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"veTokenInfo","outputs":[{"internalType":"bool","name":"active","type":"bool"},{"internalType":"uint256","name":"initialEpoch","type":"uint256"},{"internalType":"uint256","name":"lastTimestampExtended","type":"uint256"},{"internalType":"address","name":"outstandingRewardsReceiver","type":"address"},{"internalType":"enum Marionette.RewardMode","name":"rewardMode","type":"uint8"},{"internalType":"enum Marionette.LockMode","name":"lockMode","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"voteMany","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"weights","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60c060405234801562000010575f80fd5b5060405162003e9038038062003e908339810160408190526200003391620001b3565b6001600160a01b038216608052816200004c3062000060565b506001600160a01b031660a05250620001f0565b620000d881620000d26040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182525f80825260016020830181905292820192909252606081018290526080810182905260a081019190915290565b620000db565b50565b805115156001609f1b83161515141580620001045750602081015115156001609e1b8316151514155b806200011e5750604081015115156001609d1b8316151514155b80620001385750606081015115156001609c1b8316151514155b80620001525750608081015115156001609b1b8316151514155b806200016c575060a081015115156001609a1b8316151514155b156200019a57604051635378ce4160e01b81526001600160a01b038316600482015260240160405180910390fd5b5050565b6001600160a01b0381168114620000d8575f80fd5b5f8060408385031215620001c5575f80fd5b8251620001d2816200019e565b6020840151909250620001e5816200019e565b809150509250929050565b60805160a051613c316200025f5f395f81816103c601528181610965015261113601525f818161027d0152818161054f0152818161088701528181610a1101528181610b5d01528181610cc201528181610ded01528181610ef90152818161102501526111c20152613c315ff3fe608060405234801561000f575f80fd5b50600436106101a5575f3560e01c806385ad8432116100e8578063b80a75a711610093578063cbcbecb91161006e578063cbcbecb9146103c1578063dd5d55fe146103e8578063e8868a7914610450578063fd6bfc5614610473575f80fd5b8063b80a75a714610380578063c027774f14610393578063c863657d146103a6575f80fd5b806399df0bd5116100c357806399df0bd514610347578063b05391871461035a578063b5f163ff1461036d575f80fd5b806385ad8432146103025780638e5ac18d146103155780638ebf2fd61461032c575f80fd5b80632fc0945c116101535780634f1951051161012e5780634f195105146102c5578063526e0705146102d35780636b10f373146102e1578063827e0eb2146102f4575f80fd5b80632fc0945c14610278578063343b278f1461029f57806346ebfce6146102b2575f80fd5b80631cff79cd116101835780631cff79cd146102275780631e817d7a1461023a5780632b3297f91461024d575f80fd5b806301552220146101a95780630c5bcdda146101da578063150b7a02146101ef575b5f80fd5b6101bc6101b7366004612b8c565b610511565b6040516001600160e01b031990911681526020015b60405180910390f35b6101ed6101e8366004612e60565b610544565b005b6101bc6101fd366004612ed1565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6101bc610235366004612f3f565b610681565b6101bc61024836600461315d565b61087b565b600454610260906001600160a01b031681565b6040516001600160a01b0390911681526020016101d1565b6102607f000000000000000000000000000000000000000000000000000000000000000081565b6101bc6102ad366004612ed1565b610a05565b6101ed6102c036600461321b565b610b52565b6101bc6101b7366004613262565b6101bc6101b7366004612f3f565b6101ed6102ef36600461321b565b610cb7565b6101bc6101b73660046132b7565b6101bc6103103660046132f7565b610de1565b61031e60015481565b6040519081526020016101d1565b61026073b84194e28f624bbba3c9181f3a1120ee7646933781565b6101ed610355366004613366565b610eee565b6102606103683660046133d9565b610fbd565b61031e61037b3660046133d9565b610fe5565b6101ed61038e3660046133f0565b611004565b6101bc6103a1366004613474565b6111b6565b610260730966cae7338518961c2d35493d3eb481a75bb86b81565b6102607f000000000000000000000000000000000000000000000000000000000000000081565b61043e6103f63660046133d9565b5f60208190529081526040902080546001820154600283015460039093015460ff9283169391926001600160a01b03821691600160a01b8104821691600160a81b9091041686565b6040516101d19695949392919061353b565b61046361045e3660046133d9565b61155f565b60405190151581526020016101d1565b6040805160c080820183525f80835260208084018290528385018290526060808501839052608080860184905260a0958601849052865180860188528481526001818501818152828a01918252828501878152838501888152938a018881528b5198895291511515968801969096529051151598860198909852925115159184019190915290511515908201529251151591830191909152016101d1565b5f6040517f31040f8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461058d57604051633bb3ed7b60e21b815260040160405180910390fd5b6040517f6a9b8ce50000000000000000000000000000000000000000000000000000000081526001600160a01b03841690636a9b8ce5906105d4903090859060040161358a565b5f604051808303815f87803b1580156105eb575f80fd5b505af11580156105fd573d5f803e3d5ffd5b5050835191505f90505b8181101561067a576004548351610672916001600160a01b03169085908490811061063457610634613657565b60200260200101516060015186848151811061065257610652613657565b60200260200101516001600160a01b031661156f9092919063ffffffff16565b600101610607565b5050505050565b5f8061068f83850185613687565b90507f94ef0c8d000000000000000000000000000000000000000000000000000000006001600160e01b03198216016106e1575f6106cf848601866136a0565b9150506106db81610cb7565b5061084f565b7ff3a43226000000000000000000000000000000000000000000000000000000006001600160e01b031982160161073b575f8080610721868801886136eb565b93509350935050610733838383610544565b50505061084f565b7f47f58a59000000000000000000000000000000000000000000000000000000006001600160e01b031982160161079a575f80808061077c8789018961376d565b94509450945094505061079184848484611004565b5050505061084f565b7fb914031a000000000000000000000000000000000000000000000000000000006001600160e01b03198216016107e4575f6107d8848601866136a0565b9150506106db81610b52565b7f6620f42b000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610836575f808061082486880188613803565b93509350935050610733838383610eee565b60405163b4fa3fb360e01b815260040160405180910390fd5b507f1cff79cd0000000000000000000000000000000000000000000000000000000090505b9392505050565b5f336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108c557604051633bb3ed7b60e21b815260040160405180910390fd5b6108d6865f015187602001516115e3565b60208501515115610907575f85602001518060200190518101906108fa919061387a565b90506109058161166a565b505b5f610914838501856133d9565b60018190556040517f095ea7b3000000000000000000000000000000000000000000000000000000008152730966cae7338518961c2d35493d3eb481a75bb86b60048201525f1960248201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063095ea7b3906044016020604051808303815f875af11580156109b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109d79190613895565b507f1e817d7a0000000000000000000000000000000000000000000000000000000098975050505050505050565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a4f57604051633bb3ed7b60e21b815260040160405180910390fd5b6001600160a01b038616610ab6575f80610a6b848601866138b0565b90925090505f82516003811115610a8457610a8461350b565b03610a9d5781518151610a989188916116cc565b610aaf565b610aaf8787845f0151845f01516117ec565b5050610b27565b6001600160a01b038516610b27575f80858152602081905260409020600390810154600160a01b900460ff1690811115610af257610af261350b565b03610b0557610b0084611ab2565b610b27565b610b0e84611b26565b15610b1d57610b008685611c5a565b610b278487611dc7565b507f343b278f0000000000000000000000000000000000000000000000000000000095945050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610b9b57604051633bb3ed7b60e21b815260040160405180910390fd5b80515f5b81811015610cb25760025f80858481518110610bbd57610bbd613657565b602002602001015181526020019081526020015f2060030160159054906101000a900460ff166003811115610bf457610bf461350b565b1480610c4a575060015f80858481518110610c1157610c11613657565b602002602001015181526020019081526020015f2060030160159054906101000a900460ff166003811115610c4857610c4861350b565b145b8015610c8357505f80848381518110610c6557610c65613657565b60209081029190910181015182528101919091526040015f205460ff165b15610caa57610caa838281518110610c9d57610c9d613657565b6020026020010151611fe9565b600101610b9f565b505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610d0057604051633bb3ed7b60e21b815260040160405180910390fd5b80515f5b81811015610cb2575f838281518110610d1f57610d1f613657565b6020908102919091018101515f8181529182905260409091205490915060ff1615610dd8576040516316b7a01160e01b815230600482015260248101829052730966cae7338518961c2d35493d3eb481a75bb86b906316b7a011906044016020604051808303815f875af1158015610d99573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dbd9190613895565b610dcf57610dca81611ab2565b610dd8565b610dd8816122ec565b50600101610d04565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610e2b57604051633bb3ed7b60e21b815260040160405180910390fd5b5f80610e3986880188613906565b509092509050610e4f6040890160208a0161397e565b15610e855760208101515115610e85575f8160200151806020019051810190610e78919061387a565b9050610e838161166a565b505b610e92602089018961397e565b15610ea857610ea8825f015183602001516115e3565b8315610ec0575f610ebb858701876133d9565b600155505b507f85ad84320000000000000000000000000000000000000000000000000000000098975050505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610f3757604051633bb3ed7b60e21b815260040160405180910390fd5b82515f5b81811015610f7b57610f7383858381518110610f5957610f59613657565b602002602001015187848151811061065257610652613657565b600101610f3b565b507f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb848484604051610faf93929190613a0b565b60405180910390a150505050565b60028181548110610fcc575f80fd5b5f918252602090912001546001600160a01b0316905081565b60038181548110610ff4575f80fd5b5f91825260209091200154905081565b6004546001600160a01b031633148015906110485750336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614155b1561107f576040517fe906503000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83518115611160575f805b82811015611128575f808883815181106110a6576110a6613657565b60209081029190910181015182528101919091526040015f205460ff166110f9576040517f237e6c2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85818151811061110b5761110b613657565b60200260200101518261111e9190613a5d565b915060010161108a565b5061115e6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016853084612584565b505b5f5b818110156111ae576111a686828151811061117f5761117f613657565b602002602001015186838151811061119957611199613657565b60200260200101516125c3565b600101611162565b505050505050565b5f336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461120057604051633bb3ed7b60e21b815260040160405180910390fd5b5f878152602081905260408120908061121b878901896138b0565b90925090506003825160038111156112355761123561350b565b036112535760405163b4fa3fb360e01b815260040160405180910390fd5b61126360408a0160208b0161397e565b15611384575f600384810154600160a01b900460ff16908111156112895761128961350b565b1480156112a857505f825160038111156112a5576112a561350b565b14155b156112da576112b68a611ab2565b815160038401546112d5918d918d9190600160a81b900460ff166117ec565b611384565b6002600384810154600160a01b900460ff16908111156112fc576112fc61350b565b14801561131b57506001825160038111156113195761131961350b565b145b15801561136657506001600384810154600160a01b900460ff16908111156113455761134561350b565b14801561136457506002825160038111156113625761136261350b565b145b155b156113845760405163b4fa3fb360e01b815260040160405180910390fd5b61139460608a0160408b0161397e565b156114d4576001600384810154600160a81b900460ff16908111156113bb576113bb61350b565b141580156113db57506001815160038111156113d9576113d961350b565b145b1561141f57805160038085018054909160ff60a81b1990911690600160a81b90849081111561140c5761140c61350b565b021790555061141a8a611fe9565b6114d4565b6002600384810154600160a81b900460ff16908111156114415761144161350b565b14158015611461575060028151600381111561145f5761145f61350b565b145b156114a257426002840155805160038085018054909160ff60a81b1990911690600160a81b9084908111156114985761149861350b565b02179055506114d4565b805160038085018054909160ff60a81b1990911690600160a81b9084908111156114ce576114ce61350b565b02179055505b6114e160208a018a61397e565b8061151157506114f760608a0160408b0161397e565b158015611511575061150f60408a0160208b0161397e565b155b1561152f5760405163b4fa3fb360e01b815260040160405180910390fd5b507fc027774f000000000000000000000000000000000000000000000000000000009a9950505050505050505050565b5f61156982611b26565b92915050565b6040516001600160a01b03838116602483015260448201839052610cb291859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061267a565b80518251146116055760405163b4fa3fb360e01b815260040160405180910390fd5b8151611618906002906020850190612a60565b50805161162c906003906020840190612ad0565b507f48401c66c59a655bc0d105d6c3c4c501203d6d6c4bd5774ac28592a2d5c56ecc828260405161165e929190613a70565b60405180910390a15050565b6004805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f211f06c051495b535b79192c1a4531d819d569657ff4bd16daa8e9e5e6ed2bfd906020015b60405180910390a150565b6116d630846126f9565b6116df836127b1565b60038260038111156116f3576116f361350b565b036117115760405163b4fa3fb360e01b815260040160405180910390fd5b60018160038111156117255761172561350b565b036117335761173383611fe9565b5f838152602081905260409020805460ff1916600117815560038082018054859260ff60a01b1990911690600160a01b9084908111156117755761177561350b565b0217905550818160030160156101000a81548160ff021916908360038111156117a0576117a061350b565b02179055506117ad6128af565b60018201554260028201556040517ff104cdc51204c7bbdd44ca72ad5568fe0e9c646ff1b015c4c94495b6363e61f290610faf90869086908690613a9d565b6117f5836127b1565b60038260038111156118095761180961350b565b036118275760405163b4fa3fb360e01b815260040160405180910390fd5b600181600381111561183b5761183b61350b565b036118495761184983611fe9565b6040517f8fbb38ff00000000000000000000000000000000000000000000000000000000815260048101849052730966cae7338518961c2d35493d3eb481a75bb86b90638fbb38ff90602401602060405180830381865afa1580156118b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d49190613895565b1561193a5760405163310bd74b60e01b81526004810184905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611923575f80fd5b505af1158015611935573d5f803e3d5ffd5b505050505b604051635c46a7ef60e11b81526001600160a01b038516600482015230602482015260448101849052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b9063b88d4fde9060a4015f604051808303815f87803b1580156119a6575f80fd5b505af11580156119b8573d5f803e3d5ffd5b5050505f848152602081905260409020805460ff19166001178155600380820180549293508592909160ff60a01b1990911690600160a01b908490811115611a0257611a0261350b565b0217905550818160030160156101000a81548160ff02191690836003811115611a2d57611a2d61350b565b021790555042600282015560038101805473ffffffffffffffffffffffffffffffffffffffff1916905560018101545f03611a7057611a6a6128af565b60018201555b7fa9f990139fed927aa94ee2b7d6ccb4d0cb096c51726a86e0122ac2208c2dfe94848484604051611aa393929190613a9d565b60405180910390a15050505050565b5f81815260208181526040808320805460ff191681556001810184905560028101939093556003909201805475ffffffffffffffffffffffffffffffffffffffffffff1916905590518281527fb26e1d7de33ad12bab10008140743de8f6a72d95d6c4d2d3b793aab536dc7add91016116c1565b5f806001611b326128af565b611b3c9190613aca565b5f848152602081815260408083206001845260058101808452828520868652845282852054600286529084528285208686529093529083205493945092611b839190613a5d565b60015f818152600585016020526040812092935091908290611ba59087613aca565b81526020019081526020015f2054836005015f60026003811115611bcb57611bcb61350b565b6003811115611bdc57611bdc61350b565b81526020019081526020015f205f600187611bf79190613aca565b81526020019081526020015f2054611c0f9190613a5d565b90505f80600385810154600160a01b900460ff1690811115611c3357611c3361350b565b14905082151582158280611c4d575081158015611c4d5750805b9998505050505050505050565b5f8181526020819052604090205460ff1615611d535760405163310bd74b60e01b81526004810182905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611cba575f80fd5b505af1158015611ccc573d5f803e3d5ffd5b5050604051635c46a7ef60e11b81523060048201526001600160a01b038516602482015260448101849052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b925063b88d4fde915060a4015f604051808303815f87803b158015611d3c575f80fd5b505af1158015611d4e573d5f803e3d5ffd5b505050505b5f81815260208181526040808320805460ff191681556001810184905560028101939093556003909201805475ffffffffffffffffffffffffffffffffffffffffffff1916905590518281527f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d910161165e565b5f828152602081905260408120805460ff1916815560038101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03851617905590611e0f6128af565b90505f8260030160149054906101000a900460ff1690505f836005015f836003811115611e3e57611e3e61350b565b6003811115611e4f57611e4f61350b565b81526020019081526020015f205f8481526020019081526020015f205490505f811115611ebd575f846005015f846003811115611e8e57611e8e61350b565b6003811115611e9f57611e9f61350b565b815260208082019290925260409081015f9081208782529092529020555b60405163310bd74b60e01b81526004810187905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611f07575f80fd5b505af1158015611f19573d5f803e3d5ffd5b5050604051635c46a7ef60e11b81523060048201526001600160a01b038816602482015260448101899052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b925063b88d4fde915060a4015f604051808303815f87803b158015611f89575f80fd5b505af1158015611f9b573d5f803e3d5ffd5b5050604080518981526001600160a01b03891660208201527f08e544170e8e5e6c868a252670b8aa06077b96dee38dcbb4b645909db2fb3481935001905060405180910390a1505050505050565b5f818152602081905260408082209051635a2d1e0760e11b815260048101849052909190730966cae7338518961c2d35493d3eb481a75bb86b9063b45a3c0e906024016040805180830381865afa158015612046573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061206a9190613add565b60038301546002840154919250600160a81b900460ff16905f61209062093a8083613b1a565b90505f6120a062093a8042613b1a565b90508082116122e3575f62093a80806120bd6301dfe20042613a5d565b6120c79190613b1a565b6120d19190613b39565b905060018560038111156120e7576120e761350b565b1480156120f75750808660200151105b156121b1575f6121074283613aca565b60405163a4d855df60e01b8152600481018b905260248101829052909150730966cae7338518961c2d35493d3eb481a75bb86b9063a4d855df906044015f604051808303815f87803b15801561215b575f80fd5b505af115801561216d573d5f803e3d5ffd5b5050604080518c8152602081018590527f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d935001905060405180910390a1506122e1565b60028560038111156121c5576121c561350b565b036122e1575f62093a808062093a80878a602001516121e49190613aca565b6121ee9190613b1a565b6121f89190613b39565b6122029190613a5d565b90506301dfe20081111561223857818760200151101561222d576122264283613aca565b9050612238565b505050505050505050565b42600289015560405163a4d855df60e01b8152600481018a905260248101829052730966cae7338518961c2d35493d3eb481a75bb86b9063a4d855df906044015f604051808303815f87803b15801561228f575f80fd5b505af11580156122a1573d5f803e3d5ffd5b5050604080518c8152602081018590527f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d935001905060405180910390a1505b505b50505050505050565b6040517fe7e242d4000000000000000000000000000000000000000000000000000000008152600481018290525f90730966cae7338518961c2d35493d3eb481a75bb86b9063e7e242d490602401602060405180830381865afa158015612355573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123799190613b50565b90506001548110612580575f82815260208190526040812060038101549091600160a01b90910460ff16908160038111156123b6576123b661350b565b146124c6575f6123c46128af565b905083836005015f8460038111156123de576123de61350b565b60038111156123ef576123ef61350b565b815260208082019290925260409081015f90812085825290925290205560028260038111156124205761242061350b565b036124685760015f908152600584016020908152604080832084845290915290205480156124665760015f90815260058501602090815260408083208584529091528120555b505b600182600381111561247c5761247c61350b565b036124c45760025f908152600584016020908152604080832084845290915290205480156124c25760025f90815260058501602090815260408083208584529091528120555b505b505b6040517f7ac09bf700000000000000000000000000000000000000000000000000000000815273b84194e28f624bbba3c9181f3a1120ee7646933790637ac09bf79061251c908790600290600390600401613b67565b5f604051808303815f87803b158015612533575f80fd5b505af1158015612545573d5f803e3d5ffd5b505060408051878152602081018790527f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9350019050610faf565b5050565b6040516001600160a01b0384811660248301528381166044830152606482018390526125bd9186918216906323b872dd9060840161159c565b50505050565b6040517fa183af520000000000000000000000000000000000000000000000000000000081526004810183905260248101829052730966cae7338518961c2d35493d3eb481a75bb86b9063a183af52906044015f604051808303815f87803b15801561262d575f80fd5b505af115801561263f573d5f803e3d5ffd5b505060408051858152602081018590527f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd5935001905061165e565b5f61268e6001600160a01b03841683612929565b905080515f141580156126b25750808060200190518101906126b09190613895565b155b15610cb2576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024015b60405180910390fd5b6040516316b7a01160e01b81526001600160a01b038316600482015260248101829052730966cae7338518961c2d35493d3eb481a75bb86b906316b7a011906044016020604051808303815f875af1158015612757573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061277b9190613895565b612580576040517fe433766c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051635a2d1e0760e11b8152600481018290525f90730966cae7338518961c2d35493d3eb481a75bb86b9063b45a3c0e906024016040805180830381865afa158015612800573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128249190613add565b905061283362093a8042613a5d565b81602001511161286f576040517f0cc5b59800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001548151600f0b1015612580576040517f2566c43900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73b84194e28f624bbba3c9181f3a1120ee764693376001600160a01b031663060406186040518163ffffffff1660e01b8152600401602060405180830381865afa158015612900573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129249190613b50565b905090565b606061087483835f845f80856001600160a01b0316848660405161294d9190613bf8565b5f6040518083038185875af1925050503d805f8114612987576040519150601f19603f3d011682016040523d82523d5f602084013e61298c565b606091505b509150915061299c8683836129a6565b9695505050505050565b6060826129bb576129b682612a1b565b610874565b81511580156129d257506001600160a01b0384163b155b15612a14576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016126f0565b5080610874565b805115612a2b5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b828054828255905f5260205f20908101928215612ac0579160200282015b82811115612ac0578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190612a7e565b50612acc929150612b09565b5090565b828054828255905f5260205f20908101928215612ac0579160200282015b82811115612ac0578251825591602001919060010190612aee565b5b80821115612acc575f8155600101612b0a565b6001600160a01b0381168114612a5d575f80fd5b5f60608284031215612b41575f80fd5b50919050565b5f8083601f840112612b57575f80fd5b50813567ffffffffffffffff811115612b6e575f80fd5b602083019150836020828501011115612b85575f80fd5b9250929050565b5f805f8060608587031215612b9f575f80fd5b8435612baa81612b1d565b9350602085013567ffffffffffffffff80821115612bc6575f80fd5b612bd288838901612b31565b94506040870135915080821115612be7575f80fd5b50612bf487828801612b47565b95989497509550505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612c3757612c37612c00565b60405290565b6040805190810167ffffffffffffffff81118282101715612c3757612c37612c00565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c8957612c89612c00565b604052919050565b5f67ffffffffffffffff821115612caa57612caa612c00565b5060051b60200190565b5f82601f830112612cc3575f80fd5b81356020612cd8612cd383612c91565b612c60565b8083825260208201915060208460051b870101935086841115612cf9575f80fd5b602086015b84811015612d1e578035612d1181612b1d565b8352918301918301612cfe565b509695505050505050565b5f82601f830112612d38575f80fd5b81356020612d48612cd383612c91565b828152600592831b8501820192828201919087851115612d66575f80fd5b8387015b85811015612e5357803567ffffffffffffffff80821115612d89575f80fd5b9089019060a0828c03601f1901811315612da1575f80fd5b612da9612c14565b838901358152604080850135828b01526060808601358284015260808087013582850152938601359385851115612dde575f80fd5b84870196508f603f880112612df1575f80fd5b8b8701359550612e03612cd387612c91565b86815295891b87018301958c8101955091508f861115612e21575f80fd5b958201955b85871015612e3f5786358552958b0195938b0193612e26565b830152508752505050928401928401612d6a565b5090979650505050505050565b5f805f60608486031215612e72575f80fd5b8335612e7d81612b1d565b9250602084013567ffffffffffffffff80821115612e99575f80fd5b612ea587838801612cb4565b93506040860135915080821115612eba575f80fd5b50612ec786828701612d29565b9150509250925092565b5f805f805f60808688031215612ee5575f80fd5b8535612ef081612b1d565b94506020860135612f0081612b1d565b935060408601359250606086013567ffffffffffffffff811115612f22575f80fd5b612f2e88828901612b47565b969995985093965092949392505050565b5f805f60408486031215612f51575f80fd5b8335612f5c81612b1d565b9250602084013567ffffffffffffffff811115612f77575f80fd5b612f8386828701612b47565b9497909650939450505050565b5f82601f830112612f9f575f80fd5b81356020612faf612cd383612c91565b8083825260208201915060208460051b870101935086841115612fd0575f80fd5b602086015b84811015612d1e5780358352918301918301612fd5565b5f82601f830112612ffb575f80fd5b813567ffffffffffffffff81111561301557613015612c00565b613028601f8201601f1916602001612c60565b81815284602083860101111561303c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60608284031215613068575f80fd5b6040516060810167ffffffffffffffff828210818311171561308c5761308c612c00565b8160405282935084359150808211156130a3575f80fd5b6130af86838701612cb4565b835260208501359150808211156130c4575f80fd5b6130d086838701612f90565b602084015260408501359150808211156130e8575f80fd5b506130f585828601612fec565b6040830152505092915050565b5f60408284031215613112575f80fd5b61311a612c3d565b905081356004811061312a575f80fd5b8152602082013567ffffffffffffffff811115613145575f80fd5b61315184828501612fec565b60208301525092915050565b5f805f805f8060a08789031215613172575f80fd5b863561317d81612b1d565b9550602087013567ffffffffffffffff80821115613199575f80fd5b6131a58a838b01613058565b965060408901359150808211156131ba575f80fd5b6131c68a838b01613102565b955060608901359150808211156131db575f80fd5b6131e78a838b01613102565b945060808901359150808211156131fc575f80fd5b5061320989828a01612b47565b979a9699509497509295939492505050565b5f6020828403121561322b575f80fd5b813567ffffffffffffffff811115613241575f80fd5b61324d84828501612f90565b949350505050565b8015158114612a5d575f80fd5b5f805f8060608587031215613275575f80fd5b843561328081612b1d565b9350602085013561329081613255565b9250604085013567ffffffffffffffff8111156132ab575f80fd5b612bf487828801612b47565b5f805f80606085870312156132ca575f80fd5b84356132d581612b1d565b935060208501359250604085013567ffffffffffffffff8111156132ab575f80fd5b5f805f805f8060c0878903121561330c575f80fd5b863561331781612b1d565b95506133268860208901612b31565b9450608087013567ffffffffffffffff80821115613342575f80fd5b61334e8a838b01612b47565b909650945060a08901359150808211156131fc575f80fd5b5f805f60608486031215613378575f80fd5b833567ffffffffffffffff8082111561338f575f80fd5b61339b87838801612cb4565b945060208601359150808211156133b0575f80fd5b506133bd86828701612f90565b92505060408401356133ce81612b1d565b809150509250925092565b5f602082840312156133e9575f80fd5b5035919050565b5f805f8060808587031215613403575f80fd5b843567ffffffffffffffff8082111561341a575f80fd5b61342688838901612f90565b9550602087013591508082111561343b575f80fd5b5061344887828801612f90565b935050604085013561345981612b1d565b9150606085013561346981613255565b939692955090935050565b5f805f805f805f60e0888a03121561348a575f80fd5b873561349581612b1d565b9650602088013595506134ab8960408a01612b31565b945060a088013567ffffffffffffffff808211156134c7575f80fd5b6134d38b838c01612b47565b909650945060c08a01359150808211156134eb575f80fd5b506134f88a828b01612b47565b989b979a50959850939692959293505050565b634e487b7160e01b5f52602160045260245ffd5b60048110612a5d57634e487b7160e01b5f52602160045260245ffd5b861515815260208101869052604081018590526001600160a01b038416606082015260c0810161356a8461351f565b8360808301526135798361351f565b8260a0830152979650505050505050565b5f60408083016001600160a01b038616845260206040818601528186518084526060935060608701915060608160051b8801018389015f5b8381101561364757898303605f1901855281518051845286810151878501528881015189850152878101518885015260809081015160a0918501829052805191850182905287019060c08501905f905b808210156136325783518352928901929189019160019190910190613612565b505095870195935050908501906001016135c2565b50909a9950505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b80356001600160e01b031981168114613682575f80fd5b919050565b5f60208284031215613697575f80fd5b6108748261366b565b5f80604083850312156136b1575f80fd5b6136ba8361366b565b9150602083013567ffffffffffffffff8111156136d5575f80fd5b6136e185828601612f90565b9150509250929050565b5f805f80608085870312156136fe575f80fd5b6137078561366b565b9350602085013561371781612b1d565b9250604085013567ffffffffffffffff80821115613733575f80fd5b61373f88838901612cb4565b93506060870135915080821115613754575f80fd5b5061376187828801612d29565b91505092959194509250565b5f805f805f60a08688031215613781575f80fd5b61378a8661366b565b9450602086013567ffffffffffffffff808211156137a6575f80fd5b6137b289838a01612f90565b955060408801359150808211156137c7575f80fd5b506137d488828901612f90565b93505060608601356137e581612b1d565b915060808601356137f581613255565b809150509295509295909350565b5f805f8060808587031215613816575f80fd5b61381f8561366b565b9350602085013567ffffffffffffffff8082111561383b575f80fd5b61384788838901612cb4565b9450604087013591508082111561385c575f80fd5b5061386987828801612f90565b925050606085013561346981612b1d565b5f6020828403121561388a575f80fd5b815161087481612b1d565b5f602082840312156138a5575f80fd5b815161087481613255565b5f80604083850312156138c1575f80fd5b823567ffffffffffffffff808211156138d8575f80fd5b6138e486838701613102565b935060208501359150808211156138f9575f80fd5b506136e185828601613102565b5f805f60608486031215613918575f80fd5b833567ffffffffffffffff8082111561392f575f80fd5b61393b87838801613058565b94506020860135915080821115613950575f80fd5b61395c87838801613102565b93506040860135915080821115613971575f80fd5b50612ec786828701613102565b5f6020828403121561398e575f80fd5b813561087481613255565b5f815180845260208085019450602084015f5b838110156139d15781516001600160a01b0316875295820195908201906001016139ac565b509495945050505050565b5f815180845260208085019450602084015f5b838110156139d1578151875295820195908201906001016139ef565b606081525f613a1d6060830186613999565b8281036020840152613a2f81866139dc565b9150506001600160a01b0383166040830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561156957611569613a49565b604081525f613a826040830185613999565b8281036020840152613a9481856139dc565b95945050505050565b83815260608101613aad8461351f565b836020830152613abc8361351f565b826040830152949350505050565b8181038181111561156957611569613a49565b5f60408284031215613aed575f80fd5b613af5612c3d565b825180600f0b8114613b05575f80fd5b81526020928301519281019290925250919050565b5f82613b3457634e487b7160e01b5f52601260045260245ffd5b500490565b808202811582820484141761156957611569613a49565b5f60208284031215613b60575f80fd5b5051919050565b5f606082018583526020606081850152818654808452608086019150875f52825f2093505f5b81811015613bb25784546001600160a01b031683526001948501949284019201613b8d565b5050848103604086015285548082525f87815283812092840194505b81811015613bea57825485529383019360019283019201613bce565b509298975050505050505050565b5f82515f5b81811015613c175760208186018101518583015201613bfd565b505f92019182525091905056fea164736f6c6343000818000a00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f720000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106101a5575f3560e01c806385ad8432116100e8578063b80a75a711610093578063cbcbecb91161006e578063cbcbecb9146103c1578063dd5d55fe146103e8578063e8868a7914610450578063fd6bfc5614610473575f80fd5b8063b80a75a714610380578063c027774f14610393578063c863657d146103a6575f80fd5b806399df0bd5116100c357806399df0bd514610347578063b05391871461035a578063b5f163ff1461036d575f80fd5b806385ad8432146103025780638e5ac18d146103155780638ebf2fd61461032c575f80fd5b80632fc0945c116101535780634f1951051161012e5780634f195105146102c5578063526e0705146102d35780636b10f373146102e1578063827e0eb2146102f4575f80fd5b80632fc0945c14610278578063343b278f1461029f57806346ebfce6146102b2575f80fd5b80631cff79cd116101835780631cff79cd146102275780631e817d7a1461023a5780632b3297f91461024d575f80fd5b806301552220146101a95780630c5bcdda146101da578063150b7a02146101ef575b5f80fd5b6101bc6101b7366004612b8c565b610511565b6040516001600160e01b031990911681526020015b60405180910390f35b6101ed6101e8366004612e60565b610544565b005b6101bc6101fd366004612ed1565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6101bc610235366004612f3f565b610681565b6101bc61024836600461315d565b61087b565b600454610260906001600160a01b031681565b6040516001600160a01b0390911681526020016101d1565b6102607f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f7281565b6101bc6102ad366004612ed1565b610a05565b6101ed6102c036600461321b565b610b52565b6101bc6101b7366004613262565b6101bc6101b7366004612f3f565b6101ed6102ef36600461321b565b610cb7565b6101bc6101b73660046132b7565b6101bc6103103660046132f7565b610de1565b61031e60015481565b6040519081526020016101d1565b61026073b84194e28f624bbba3c9181f3a1120ee7646933781565b6101ed610355366004613366565b610eee565b6102606103683660046133d9565b610fbd565b61031e61037b3660046133d9565b610fe5565b6101ed61038e3660046133f0565b611004565b6101bc6103a1366004613474565b6111b6565b610260730966cae7338518961c2d35493d3eb481a75bb86b81565b6102607f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba81565b61043e6103f63660046133d9565b5f60208190529081526040902080546001820154600283015460039093015460ff9283169391926001600160a01b03821691600160a01b8104821691600160a81b9091041686565b6040516101d19695949392919061353b565b61046361045e3660046133d9565b61155f565b60405190151581526020016101d1565b6040805160c080820183525f80835260208084018290528385018290526060808501839052608080860184905260a0958601849052865180860188528481526001818501818152828a01918252828501878152838501888152938a018881528b5198895291511515968801969096529051151598860198909852925115159184019190915290511515908201529251151591830191909152016101d1565b5f6040517f31040f8000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f72161461058d57604051633bb3ed7b60e21b815260040160405180910390fd5b6040517f6a9b8ce50000000000000000000000000000000000000000000000000000000081526001600160a01b03841690636a9b8ce5906105d4903090859060040161358a565b5f604051808303815f87803b1580156105eb575f80fd5b505af11580156105fd573d5f803e3d5ffd5b5050835191505f90505b8181101561067a576004548351610672916001600160a01b03169085908490811061063457610634613657565b60200260200101516060015186848151811061065257610652613657565b60200260200101516001600160a01b031661156f9092919063ffffffff16565b600101610607565b5050505050565b5f8061068f83850185613687565b90507f94ef0c8d000000000000000000000000000000000000000000000000000000006001600160e01b03198216016106e1575f6106cf848601866136a0565b9150506106db81610cb7565b5061084f565b7ff3a43226000000000000000000000000000000000000000000000000000000006001600160e01b031982160161073b575f8080610721868801886136eb565b93509350935050610733838383610544565b50505061084f565b7f47f58a59000000000000000000000000000000000000000000000000000000006001600160e01b031982160161079a575f80808061077c8789018961376d565b94509450945094505061079184848484611004565b5050505061084f565b7fb914031a000000000000000000000000000000000000000000000000000000006001600160e01b03198216016107e4575f6107d8848601866136a0565b9150506106db81610b52565b7f6620f42b000000000000000000000000000000000000000000000000000000006001600160e01b0319821601610836575f808061082486880188613803565b93509350935050610733838383610eee565b60405163b4fa3fb360e01b815260040160405180910390fd5b507f1cff79cd0000000000000000000000000000000000000000000000000000000090505b9392505050565b5f336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f7216146108c557604051633bb3ed7b60e21b815260040160405180910390fd5b6108d6865f015187602001516115e3565b60208501515115610907575f85602001518060200190518101906108fa919061387a565b90506109058161166a565b505b5f610914838501856133d9565b60018190556040517f095ea7b3000000000000000000000000000000000000000000000000000000008152730966cae7338518961c2d35493d3eb481a75bb86b60048201525f1960248201529091507f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba6001600160a01b03169063095ea7b3906044016020604051808303815f875af11580156109b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109d79190613895565b507f1e817d7a0000000000000000000000000000000000000000000000000000000098975050505050505050565b5f336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614610a4f57604051633bb3ed7b60e21b815260040160405180910390fd5b6001600160a01b038616610ab6575f80610a6b848601866138b0565b90925090505f82516003811115610a8457610a8461350b565b03610a9d5781518151610a989188916116cc565b610aaf565b610aaf8787845f0151845f01516117ec565b5050610b27565b6001600160a01b038516610b27575f80858152602081905260409020600390810154600160a01b900460ff1690811115610af257610af261350b565b03610b0557610b0084611ab2565b610b27565b610b0e84611b26565b15610b1d57610b008685611c5a565b610b278487611dc7565b507f343b278f0000000000000000000000000000000000000000000000000000000095945050505050565b336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614610b9b57604051633bb3ed7b60e21b815260040160405180910390fd5b80515f5b81811015610cb25760025f80858481518110610bbd57610bbd613657565b602002602001015181526020019081526020015f2060030160159054906101000a900460ff166003811115610bf457610bf461350b565b1480610c4a575060015f80858481518110610c1157610c11613657565b602002602001015181526020019081526020015f2060030160159054906101000a900460ff166003811115610c4857610c4861350b565b145b8015610c8357505f80848381518110610c6557610c65613657565b60209081029190910181015182528101919091526040015f205460ff165b15610caa57610caa838281518110610c9d57610c9d613657565b6020026020010151611fe9565b600101610b9f565b505050565b336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614610d0057604051633bb3ed7b60e21b815260040160405180910390fd5b80515f5b81811015610cb2575f838281518110610d1f57610d1f613657565b6020908102919091018101515f8181529182905260409091205490915060ff1615610dd8576040516316b7a01160e01b815230600482015260248101829052730966cae7338518961c2d35493d3eb481a75bb86b906316b7a011906044016020604051808303815f875af1158015610d99573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dbd9190613895565b610dcf57610dca81611ab2565b610dd8565b610dd8816122ec565b50600101610d04565b5f336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614610e2b57604051633bb3ed7b60e21b815260040160405180910390fd5b5f80610e3986880188613906565b509092509050610e4f6040890160208a0161397e565b15610e855760208101515115610e85575f8160200151806020019051810190610e78919061387a565b9050610e838161166a565b505b610e92602089018961397e565b15610ea857610ea8825f015183602001516115e3565b8315610ec0575f610ebb858701876133d9565b600155505b507f85ad84320000000000000000000000000000000000000000000000000000000098975050505050505050565b336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614610f3757604051633bb3ed7b60e21b815260040160405180910390fd5b82515f5b81811015610f7b57610f7383858381518110610f5957610f59613657565b602002602001015187848151811061065257610652613657565b600101610f3b565b507f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb848484604051610faf93929190613a0b565b60405180910390a150505050565b60028181548110610fcc575f80fd5b5f918252602090912001546001600160a01b0316905081565b60038181548110610ff4575f80fd5b5f91825260209091200154905081565b6004546001600160a01b031633148015906110485750336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f721614155b1561107f576040517fe906503000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83518115611160575f805b82811015611128575f808883815181106110a6576110a6613657565b60209081029190910181015182528101919091526040015f205460ff166110f9576040517f237e6c2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b85818151811061110b5761110b613657565b60200260200101518261111e9190613a5d565b915060010161108a565b5061115e6001600160a01b037f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba16853084612584565b505b5f5b818110156111ae576111a686828151811061117f5761117f613657565b602002602001015186838151811061119957611199613657565b60200260200101516125c3565b600101611162565b505050505050565b5f336001600160a01b037f00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f72161461120057604051633bb3ed7b60e21b815260040160405180910390fd5b5f878152602081905260408120908061121b878901896138b0565b90925090506003825160038111156112355761123561350b565b036112535760405163b4fa3fb360e01b815260040160405180910390fd5b61126360408a0160208b0161397e565b15611384575f600384810154600160a01b900460ff16908111156112895761128961350b565b1480156112a857505f825160038111156112a5576112a561350b565b14155b156112da576112b68a611ab2565b815160038401546112d5918d918d9190600160a81b900460ff166117ec565b611384565b6002600384810154600160a01b900460ff16908111156112fc576112fc61350b565b14801561131b57506001825160038111156113195761131961350b565b145b15801561136657506001600384810154600160a01b900460ff16908111156113455761134561350b565b14801561136457506002825160038111156113625761136261350b565b145b155b156113845760405163b4fa3fb360e01b815260040160405180910390fd5b61139460608a0160408b0161397e565b156114d4576001600384810154600160a81b900460ff16908111156113bb576113bb61350b565b141580156113db57506001815160038111156113d9576113d961350b565b145b1561141f57805160038085018054909160ff60a81b1990911690600160a81b90849081111561140c5761140c61350b565b021790555061141a8a611fe9565b6114d4565b6002600384810154600160a81b900460ff16908111156114415761144161350b565b14158015611461575060028151600381111561145f5761145f61350b565b145b156114a257426002840155805160038085018054909160ff60a81b1990911690600160a81b9084908111156114985761149861350b565b02179055506114d4565b805160038085018054909160ff60a81b1990911690600160a81b9084908111156114ce576114ce61350b565b02179055505b6114e160208a018a61397e565b8061151157506114f760608a0160408b0161397e565b158015611511575061150f60408a0160208b0161397e565b155b1561152f5760405163b4fa3fb360e01b815260040160405180910390fd5b507fc027774f000000000000000000000000000000000000000000000000000000009a9950505050505050505050565b5f61156982611b26565b92915050565b6040516001600160a01b03838116602483015260448201839052610cb291859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061267a565b80518251146116055760405163b4fa3fb360e01b815260040160405180910390fd5b8151611618906002906020850190612a60565b50805161162c906003906020840190612ad0565b507f48401c66c59a655bc0d105d6c3c4c501203d6d6c4bd5774ac28592a2d5c56ecc828260405161165e929190613a70565b60405180910390a15050565b6004805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040519081527f211f06c051495b535b79192c1a4531d819d569657ff4bd16daa8e9e5e6ed2bfd906020015b60405180910390a150565b6116d630846126f9565b6116df836127b1565b60038260038111156116f3576116f361350b565b036117115760405163b4fa3fb360e01b815260040160405180910390fd5b60018160038111156117255761172561350b565b036117335761173383611fe9565b5f838152602081905260409020805460ff1916600117815560038082018054859260ff60a01b1990911690600160a01b9084908111156117755761177561350b565b0217905550818160030160156101000a81548160ff021916908360038111156117a0576117a061350b565b02179055506117ad6128af565b60018201554260028201556040517ff104cdc51204c7bbdd44ca72ad5568fe0e9c646ff1b015c4c94495b6363e61f290610faf90869086908690613a9d565b6117f5836127b1565b60038260038111156118095761180961350b565b036118275760405163b4fa3fb360e01b815260040160405180910390fd5b600181600381111561183b5761183b61350b565b036118495761184983611fe9565b6040517f8fbb38ff00000000000000000000000000000000000000000000000000000000815260048101849052730966cae7338518961c2d35493d3eb481a75bb86b90638fbb38ff90602401602060405180830381865afa1580156118b0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118d49190613895565b1561193a5760405163310bd74b60e01b81526004810184905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611923575f80fd5b505af1158015611935573d5f803e3d5ffd5b505050505b604051635c46a7ef60e11b81526001600160a01b038516600482015230602482015260448101849052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b9063b88d4fde9060a4015f604051808303815f87803b1580156119a6575f80fd5b505af11580156119b8573d5f803e3d5ffd5b5050505f848152602081905260409020805460ff19166001178155600380820180549293508592909160ff60a01b1990911690600160a01b908490811115611a0257611a0261350b565b0217905550818160030160156101000a81548160ff02191690836003811115611a2d57611a2d61350b565b021790555042600282015560038101805473ffffffffffffffffffffffffffffffffffffffff1916905560018101545f03611a7057611a6a6128af565b60018201555b7fa9f990139fed927aa94ee2b7d6ccb4d0cb096c51726a86e0122ac2208c2dfe94848484604051611aa393929190613a9d565b60405180910390a15050505050565b5f81815260208181526040808320805460ff191681556001810184905560028101939093556003909201805475ffffffffffffffffffffffffffffffffffffffffffff1916905590518281527fb26e1d7de33ad12bab10008140743de8f6a72d95d6c4d2d3b793aab536dc7add91016116c1565b5f806001611b326128af565b611b3c9190613aca565b5f848152602081815260408083206001845260058101808452828520868652845282852054600286529084528285208686529093529083205493945092611b839190613a5d565b60015f818152600585016020526040812092935091908290611ba59087613aca565b81526020019081526020015f2054836005015f60026003811115611bcb57611bcb61350b565b6003811115611bdc57611bdc61350b565b81526020019081526020015f205f600187611bf79190613aca565b81526020019081526020015f2054611c0f9190613a5d565b90505f80600385810154600160a01b900460ff1690811115611c3357611c3361350b565b14905082151582158280611c4d575081158015611c4d5750805b9998505050505050505050565b5f8181526020819052604090205460ff1615611d535760405163310bd74b60e01b81526004810182905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611cba575f80fd5b505af1158015611ccc573d5f803e3d5ffd5b5050604051635c46a7ef60e11b81523060048201526001600160a01b038516602482015260448101849052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b925063b88d4fde915060a4015f604051808303815f87803b158015611d3c575f80fd5b505af1158015611d4e573d5f803e3d5ffd5b505050505b5f81815260208181526040808320805460ff191681556001810184905560028101939093556003909201805475ffffffffffffffffffffffffffffffffffffffffffff1916905590518281527f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d910161165e565b5f828152602081905260408120805460ff1916815560038101805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03851617905590611e0f6128af565b90505f8260030160149054906101000a900460ff1690505f836005015f836003811115611e3e57611e3e61350b565b6003811115611e4f57611e4f61350b565b81526020019081526020015f205f8481526020019081526020015f205490505f811115611ebd575f846005015f846003811115611e8e57611e8e61350b565b6003811115611e9f57611e9f61350b565b815260208082019290925260409081015f9081208782529092529020555b60405163310bd74b60e01b81526004810187905273b84194e28f624bbba3c9181f3a1120ee764693379063310bd74b906024015f604051808303815f87803b158015611f07575f80fd5b505af1158015611f19573d5f803e3d5ffd5b5050604051635c46a7ef60e11b81523060048201526001600160a01b038816602482015260448101899052608060648201525f6084820152730966cae7338518961c2d35493d3eb481a75bb86b925063b88d4fde915060a4015f604051808303815f87803b158015611f89575f80fd5b505af1158015611f9b573d5f803e3d5ffd5b5050604080518981526001600160a01b03891660208201527f08e544170e8e5e6c868a252670b8aa06077b96dee38dcbb4b645909db2fb3481935001905060405180910390a1505050505050565b5f818152602081905260408082209051635a2d1e0760e11b815260048101849052909190730966cae7338518961c2d35493d3eb481a75bb86b9063b45a3c0e906024016040805180830381865afa158015612046573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061206a9190613add565b60038301546002840154919250600160a81b900460ff16905f61209062093a8083613b1a565b90505f6120a062093a8042613b1a565b90508082116122e3575f62093a80806120bd6301dfe20042613a5d565b6120c79190613b1a565b6120d19190613b39565b905060018560038111156120e7576120e761350b565b1480156120f75750808660200151105b156121b1575f6121074283613aca565b60405163a4d855df60e01b8152600481018b905260248101829052909150730966cae7338518961c2d35493d3eb481a75bb86b9063a4d855df906044015f604051808303815f87803b15801561215b575f80fd5b505af115801561216d573d5f803e3d5ffd5b5050604080518c8152602081018590527f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d935001905060405180910390a1506122e1565b60028560038111156121c5576121c561350b565b036122e1575f62093a808062093a80878a602001516121e49190613aca565b6121ee9190613b1a565b6121f89190613b39565b6122029190613a5d565b90506301dfe20081111561223857818760200151101561222d576122264283613aca565b9050612238565b505050505050505050565b42600289015560405163a4d855df60e01b8152600481018a905260248101829052730966cae7338518961c2d35493d3eb481a75bb86b9063a4d855df906044015f604051808303815f87803b15801561228f575f80fd5b505af11580156122a1573d5f803e3d5ffd5b5050604080518c8152602081018590527f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d935001905060405180910390a1505b505b50505050505050565b6040517fe7e242d4000000000000000000000000000000000000000000000000000000008152600481018290525f90730966cae7338518961c2d35493d3eb481a75bb86b9063e7e242d490602401602060405180830381865afa158015612355573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123799190613b50565b90506001548110612580575f82815260208190526040812060038101549091600160a01b90910460ff16908160038111156123b6576123b661350b565b146124c6575f6123c46128af565b905083836005015f8460038111156123de576123de61350b565b60038111156123ef576123ef61350b565b815260208082019290925260409081015f90812085825290925290205560028260038111156124205761242061350b565b036124685760015f908152600584016020908152604080832084845290915290205480156124665760015f90815260058501602090815260408083208584529091528120555b505b600182600381111561247c5761247c61350b565b036124c45760025f908152600584016020908152604080832084845290915290205480156124c25760025f90815260058501602090815260408083208584529091528120555b505b505b6040517f7ac09bf700000000000000000000000000000000000000000000000000000000815273b84194e28f624bbba3c9181f3a1120ee7646933790637ac09bf79061251c908790600290600390600401613b67565b5f604051808303815f87803b158015612533575f80fd5b505af1158015612545573d5f803e3d5ffd5b505060408051878152602081018790527f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9350019050610faf565b5050565b6040516001600160a01b0384811660248301528381166044830152606482018390526125bd9186918216906323b872dd9060840161159c565b50505050565b6040517fa183af520000000000000000000000000000000000000000000000000000000081526004810183905260248101829052730966cae7338518961c2d35493d3eb481a75bb86b9063a183af52906044015f604051808303815f87803b15801561262d575f80fd5b505af115801561263f573d5f803e3d5ffd5b505060408051858152602081018590527f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd5935001905061165e565b5f61268e6001600160a01b03841683612929565b905080515f141580156126b25750808060200190518101906126b09190613895565b155b15610cb2576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024015b60405180910390fd5b6040516316b7a01160e01b81526001600160a01b038316600482015260248101829052730966cae7338518961c2d35493d3eb481a75bb86b906316b7a011906044016020604051808303815f875af1158015612757573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061277b9190613895565b612580576040517fe433766c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604051635a2d1e0760e11b8152600481018290525f90730966cae7338518961c2d35493d3eb481a75bb86b9063b45a3c0e906024016040805180830381865afa158015612800573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128249190613add565b905061283362093a8042613a5d565b81602001511161286f576040517f0cc5b59800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001548151600f0b1015612580576040517f2566c43900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73b84194e28f624bbba3c9181f3a1120ee764693376001600160a01b031663060406186040518163ffffffff1660e01b8152600401602060405180830381865afa158015612900573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129249190613b50565b905090565b606061087483835f845f80856001600160a01b0316848660405161294d9190613bf8565b5f6040518083038185875af1925050503d805f8114612987576040519150601f19603f3d011682016040523d82523d5f602084013e61298c565b606091505b509150915061299c8683836129a6565b9695505050505050565b6060826129bb576129b682612a1b565b610874565b81511580156129d257506001600160a01b0384163b155b15612a14576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016126f0565b5080610874565b805115612a2b5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b828054828255905f5260205f20908101928215612ac0579160200282015b82811115612ac0578251825473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909116178255602090920191600190910190612a7e565b50612acc929150612b09565b5090565b828054828255905f5260205f20908101928215612ac0579160200282015b82811115612ac0578251825591602001919060010190612aee565b5b80821115612acc575f8155600101612b0a565b6001600160a01b0381168114612a5d575f80fd5b5f60608284031215612b41575f80fd5b50919050565b5f8083601f840112612b57575f80fd5b50813567ffffffffffffffff811115612b6e575f80fd5b602083019150836020828501011115612b85575f80fd5b9250929050565b5f805f8060608587031215612b9f575f80fd5b8435612baa81612b1d565b9350602085013567ffffffffffffffff80821115612bc6575f80fd5b612bd288838901612b31565b94506040870135915080821115612be7575f80fd5b50612bf487828801612b47565b95989497509550505050565b634e487b7160e01b5f52604160045260245ffd5b60405160a0810167ffffffffffffffff81118282101715612c3757612c37612c00565b60405290565b6040805190810167ffffffffffffffff81118282101715612c3757612c37612c00565b604051601f8201601f1916810167ffffffffffffffff81118282101715612c8957612c89612c00565b604052919050565b5f67ffffffffffffffff821115612caa57612caa612c00565b5060051b60200190565b5f82601f830112612cc3575f80fd5b81356020612cd8612cd383612c91565b612c60565b8083825260208201915060208460051b870101935086841115612cf9575f80fd5b602086015b84811015612d1e578035612d1181612b1d565b8352918301918301612cfe565b509695505050505050565b5f82601f830112612d38575f80fd5b81356020612d48612cd383612c91565b828152600592831b8501820192828201919087851115612d66575f80fd5b8387015b85811015612e5357803567ffffffffffffffff80821115612d89575f80fd5b9089019060a0828c03601f1901811315612da1575f80fd5b612da9612c14565b838901358152604080850135828b01526060808601358284015260808087013582850152938601359385851115612dde575f80fd5b84870196508f603f880112612df1575f80fd5b8b8701359550612e03612cd387612c91565b86815295891b87018301958c8101955091508f861115612e21575f80fd5b958201955b85871015612e3f5786358552958b0195938b0193612e26565b830152508752505050928401928401612d6a565b5090979650505050505050565b5f805f60608486031215612e72575f80fd5b8335612e7d81612b1d565b9250602084013567ffffffffffffffff80821115612e99575f80fd5b612ea587838801612cb4565b93506040860135915080821115612eba575f80fd5b50612ec786828701612d29565b9150509250925092565b5f805f805f60808688031215612ee5575f80fd5b8535612ef081612b1d565b94506020860135612f0081612b1d565b935060408601359250606086013567ffffffffffffffff811115612f22575f80fd5b612f2e88828901612b47565b969995985093965092949392505050565b5f805f60408486031215612f51575f80fd5b8335612f5c81612b1d565b9250602084013567ffffffffffffffff811115612f77575f80fd5b612f8386828701612b47565b9497909650939450505050565b5f82601f830112612f9f575f80fd5b81356020612faf612cd383612c91565b8083825260208201915060208460051b870101935086841115612fd0575f80fd5b602086015b84811015612d1e5780358352918301918301612fd5565b5f82601f830112612ffb575f80fd5b813567ffffffffffffffff81111561301557613015612c00565b613028601f8201601f1916602001612c60565b81815284602083860101111561303c575f80fd5b816020850160208301375f918101602001919091529392505050565b5f60608284031215613068575f80fd5b6040516060810167ffffffffffffffff828210818311171561308c5761308c612c00565b8160405282935084359150808211156130a3575f80fd5b6130af86838701612cb4565b835260208501359150808211156130c4575f80fd5b6130d086838701612f90565b602084015260408501359150808211156130e8575f80fd5b506130f585828601612fec565b6040830152505092915050565b5f60408284031215613112575f80fd5b61311a612c3d565b905081356004811061312a575f80fd5b8152602082013567ffffffffffffffff811115613145575f80fd5b61315184828501612fec565b60208301525092915050565b5f805f805f8060a08789031215613172575f80fd5b863561317d81612b1d565b9550602087013567ffffffffffffffff80821115613199575f80fd5b6131a58a838b01613058565b965060408901359150808211156131ba575f80fd5b6131c68a838b01613102565b955060608901359150808211156131db575f80fd5b6131e78a838b01613102565b945060808901359150808211156131fc575f80fd5b5061320989828a01612b47565b979a9699509497509295939492505050565b5f6020828403121561322b575f80fd5b813567ffffffffffffffff811115613241575f80fd5b61324d84828501612f90565b949350505050565b8015158114612a5d575f80fd5b5f805f8060608587031215613275575f80fd5b843561328081612b1d565b9350602085013561329081613255565b9250604085013567ffffffffffffffff8111156132ab575f80fd5b612bf487828801612b47565b5f805f80606085870312156132ca575f80fd5b84356132d581612b1d565b935060208501359250604085013567ffffffffffffffff8111156132ab575f80fd5b5f805f805f8060c0878903121561330c575f80fd5b863561331781612b1d565b95506133268860208901612b31565b9450608087013567ffffffffffffffff80821115613342575f80fd5b61334e8a838b01612b47565b909650945060a08901359150808211156131fc575f80fd5b5f805f60608486031215613378575f80fd5b833567ffffffffffffffff8082111561338f575f80fd5b61339b87838801612cb4565b945060208601359150808211156133b0575f80fd5b506133bd86828701612f90565b92505060408401356133ce81612b1d565b809150509250925092565b5f602082840312156133e9575f80fd5b5035919050565b5f805f8060808587031215613403575f80fd5b843567ffffffffffffffff8082111561341a575f80fd5b61342688838901612f90565b9550602087013591508082111561343b575f80fd5b5061344887828801612f90565b935050604085013561345981612b1d565b9150606085013561346981613255565b939692955090935050565b5f805f805f805f60e0888a03121561348a575f80fd5b873561349581612b1d565b9650602088013595506134ab8960408a01612b31565b945060a088013567ffffffffffffffff808211156134c7575f80fd5b6134d38b838c01612b47565b909650945060c08a01359150808211156134eb575f80fd5b506134f88a828b01612b47565b989b979a50959850939692959293505050565b634e487b7160e01b5f52602160045260245ffd5b60048110612a5d57634e487b7160e01b5f52602160045260245ffd5b861515815260208101869052604081018590526001600160a01b038416606082015260c0810161356a8461351f565b8360808301526135798361351f565b8260a0830152979650505050505050565b5f60408083016001600160a01b038616845260206040818601528186518084526060935060608701915060608160051b8801018389015f5b8381101561364757898303605f1901855281518051845286810151878501528881015189850152878101518885015260809081015160a0918501829052805191850182905287019060c08501905f905b808210156136325783518352928901929189019160019190910190613612565b505095870195935050908501906001016135c2565b50909a9950505050505050505050565b634e487b7160e01b5f52603260045260245ffd5b80356001600160e01b031981168114613682575f80fd5b919050565b5f60208284031215613697575f80fd5b6108748261366b565b5f80604083850312156136b1575f80fd5b6136ba8361366b565b9150602083013567ffffffffffffffff8111156136d5575f80fd5b6136e185828601612f90565b9150509250929050565b5f805f80608085870312156136fe575f80fd5b6137078561366b565b9350602085013561371781612b1d565b9250604085013567ffffffffffffffff80821115613733575f80fd5b61373f88838901612cb4565b93506060870135915080821115613754575f80fd5b5061376187828801612d29565b91505092959194509250565b5f805f805f60a08688031215613781575f80fd5b61378a8661366b565b9450602086013567ffffffffffffffff808211156137a6575f80fd5b6137b289838a01612f90565b955060408801359150808211156137c7575f80fd5b506137d488828901612f90565b93505060608601356137e581612b1d565b915060808601356137f581613255565b809150509295509295909350565b5f805f8060808587031215613816575f80fd5b61381f8561366b565b9350602085013567ffffffffffffffff8082111561383b575f80fd5b61384788838901612cb4565b9450604087013591508082111561385c575f80fd5b5061386987828801612f90565b925050606085013561346981612b1d565b5f6020828403121561388a575f80fd5b815161087481612b1d565b5f602082840312156138a5575f80fd5b815161087481613255565b5f80604083850312156138c1575f80fd5b823567ffffffffffffffff808211156138d8575f80fd5b6138e486838701613102565b935060208501359150808211156138f9575f80fd5b506136e185828601613102565b5f805f60608486031215613918575f80fd5b833567ffffffffffffffff8082111561392f575f80fd5b61393b87838801613058565b94506020860135915080821115613950575f80fd5b61395c87838801613102565b93506040860135915080821115613971575f80fd5b50612ec786828701613102565b5f6020828403121561398e575f80fd5b813561087481613255565b5f815180845260208085019450602084015f5b838110156139d15781516001600160a01b0316875295820195908201906001016139ac565b509495945050505050565b5f815180845260208085019450602084015f5b838110156139d1578151875295820195908201906001016139ef565b606081525f613a1d6060830186613999565b8281036020840152613a2f81866139dc565b9150506001600160a01b0383166040830152949350505050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561156957611569613a49565b604081525f613a826040830185613999565b8281036020840152613a9481856139dc565b95945050505050565b83815260608101613aad8461351f565b836020830152613abc8361351f565b826040830152949350505050565b8181038181111561156957611569613a49565b5f60408284031215613aed575f80fd5b613af5612c3d565b825180600f0b8114613b05575f80fd5b81526020928301519281019290925250919050565b5f82613b3457634e487b7160e01b5f52601260045260245ffd5b500490565b808202811582820484141761156957611569613a49565b5f60208284031215613b60575f80fd5b5051919050565b5f606082018583526020606081850152818654808452608086019150875f52825f2093505f5b81811015613bb25784546001600160a01b031683526001948501949284019201613b8d565b5050848103604086015285548082525f87815283812092840194505b81811015613bea57825485529383019360019283019201613bce565b509298975050505050505050565b5f82515f5b81811015613c175760208186018101518583015201613bfd565b505f92019182525091905056fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f720000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba
-----Decoded View---------------
Arg [0] : _puppeteer (address): 0x82136B5B2FA53AEFaB8d7C87467D8e7036Bb3f72
Arg [1] : initialTHE (address): 0x4D85bA8c3918359c78Ed09581E5bc7578ba932ba
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000082136b5b2fa53aefab8d7c87467d8e7036bb3f72
Arg [1] : 0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.