Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 15752898 | 307 days ago | Contract Creation | 0 S |
Cross-Chain Transactions
Loading...
Loading
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
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in S
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.