Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 37577448 | 202 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 { FeeM } from "src/FeeM.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, FeeM {
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();
error DepositFrozen();
/*//////////////////////////////////////////////////////////////
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);
event DepositPaused(bool paused);
/*//////////////////////////////////////////////////////////////
ADAPTER STORAGE
//////////////////////////////////////////////////////////////*/
IVotingEscrow public constant VE = IVotingEscrow(0x1Ec2b9a77A7226ACD457954820197F89B3E3a578);
IVoter public constant VOTER = IVoter(0x43739B96B19aE7C2E0d80BE7832325846f55Fa05);
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;
bool public depositPaused;
/*//////////////////////////////////////////////////////////////
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;
registerMe();
}
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)) {
if (depositPaused) {
revert DepositFrozen();
}
(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;
}
function setDepositPaused(bool _paused) public onlyPuppeteer {
depositPaused = _paused;
emit DepositPaused(_paused);
}
///@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 if (functionSignature == this.setDepositPaused.selector) {
(, bool _paused) = abi.decode(adapterData, (bytes4, bool));
setDepositPaused(_paused);
} 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;
abstract contract FeeM {
/// @dev Register my contract on Sonic FeeM
function registerMe() internal {
(bool _success, ) = address(0xDC2B0D2Dd2b7759D97D50db4eabDC36973110830)
.call(abi.encodeWithSignature("selfRegister(uint256)", 28));
require(_success, "FeeM registration failed");
}
}// 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": true,
"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":"DepositFrozen","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":"bool","name":"paused","type":"bool"}],"name":"DepositPaused","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":[],"name":"depositPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":[{"internalType":"bool","name":"_paused","type":"bool"}],"name":"setDepositPaused","outputs":[],"stateMutability":"nonpayable","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
60c060409080825234620002e357818162004008803803809162000024828562000303565b833981010312620002e35780516001600160a01b03908181168103620002e3576020809301519182168203620002e3576080525f60a084516200006781620002e7565b8281528285820152828682015282606082015282608082015201525f60a084516200009281620002e7565b82815260018582015260018682015282606082015282608082015201526001609f1b301615801590620002cf575b8015620002bb575b8015620002ab575b80156200029b575b80156200028b575b620002745760a05281516307983f4560e21b828201908152601c60248084019190915282526001600160401b03919060608201908382118383101762000259575f928392875251908273dc2b0d2dd2b7759d97d50db4eabdc369731108305af1903d156200026d573d908111620002595783519062000169601f8201601f191685018362000303565b81525f833d92013e5b1562000217575051613ce0908162000328823960805181818161038e015281816108ca01528181610a0801528181610b7f01528181610d3f0152818161114f015281816112610152818161136b0152818161192401528181611a0201528181611bdc01528181612053015281816123d601528181612441015281816125c1015281816126c401526127d7015260a0518181816102c9015281816108090152611ab20152f35b606491519062461bcd60e51b82526004820152601860248201527f4665654d20726567697374726174696f6e206661696c656400000000000000006044820152fd5b634e487b7160e01b5f52604160045260245ffd5b5062000172565b8251635378ce4160e01b8152306004820152602490fd5b50306001609a1b161515620000e0565b50306001609b1b161515620000d8565b50306001609c1b161515620000d0565b50306001609d1b16151560011415620000c8565b50306001609e1b16151560011415620000c0565b5f80fd5b60c081019081106001600160401b038211176200025957604052565b601f909101601f19168101906001600160401b03821190821017620002595760405256fe6080806040526004361015610012575f80fd5b5f905f3560e01c908163015522201461296c5750806302befd24146129475780630c5bcdda14612772578063150b7a02146127345780631cff79cd14611b4f5780631e817d7a1461196f5780632b3297f9146119485780632fc0945c14611904578063343b278f1461134d57806346ebfce6146112245780634f195105146111de578063526e0705146111bc578063543f66a4146111265780636b10f37314610d03578063827e0eb214610cb657806385ad843214610b0c5780638e5ac18d14610aee5780638ebf2fd614610abf57806399df0bd5146109a6578063b05391871461094b578063b5f163ff146108fa578063b80a75a714610670578063c027774f1461031c578063c863657d146102ed578063cbcbecb9146102a9578063dd5d55fe14610224578063e8868a79146101fb5763fd6bfc5614610152575f80fd5b346101f857806003193601126101f8578060c09160a060405161017481612a63565b828152826020820152826040820152826060820152826080820152015260405161019d81612a63565b8181526020810190600182526040810160018152606082019084825260a060808401938685520193858552604051958652511515602086015251151560408501525115156060840152511515608083015251151560a0820152f35b80fd5b50346101f85760203660031901126101f857602061021a600435613374565b6040519015158152f35b50346101f85760203660031901126101f857604060c0916004358152806020522060ff815416906001810154906001600160a01b036003600283015492015460ff8160a01c169260ff8260a81c169460405196151587526020870152604086015216606084015261029481612ebb565b60808301526102a281612ebb565b60a0820152f35b50346101f857806003193601126101f85760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101f857806003193601126101f8576020604051731ec2b9a77a7226acd457954820197f89b3e3a5788152f35b50346101f85760e03660031901126101f8576103366129cd565b6024359190606036604319011261066c5767ffffffffffffffff9160a43583811161066c57610369903690600401612a0d565b9360c43590811161066857610382903690600401612a0d565b50506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e57604082866103cb9452806020522093810190612f9d565b91600382516103d981612ebb565b6103e281612ebb565b14610443576103ef612fef565b610540575b50506103fe612ffe565b610473575b50505061040e612fe0565b8015610454575b6104435760206040517fc027774f000000000000000000000000000000000000000000000000000000008152f35b600460405163b4fa3fb360e01b8152fd5b5061045d612ffe565b158015610415575061046d612fef565b15610415565b600382019160ff835460a81c1661048981612ebb565b600181141580610523575b156104c15750506104b992916104b49151906104af82612ebb565b61309d565b613748565b5f8080610403565b60029194506104cf81612ebb565b141580610506575b156104f6576104f19260024291015551906104af82612ebb565b6104b9565b6104f1925051906104af82612ebb565b506002815161051481612ebb565b61051d81612ebb565b146104d7565b506001835161053181612ebb565b61053a81612ebb565b14610494565b600384018560ff825460a01c1661055681612ebb565b801580610622575b15610590575060ff610589946105738361343c565b519261057e84612ebb565b5460a81c16926134d8565b5f806103f4565b939250505061059e82612ebb565b6002821480610605575b1591826105c8575b50501561058957600460405163b4fa3fb360e01b8152fd5b60019192506105d681612ebb565b1490816105e7575b50155f806105b0565b60029150516105f581612ebb565b6105fe81612ebb565b145f6105de565b506001815161061381612ebb565b61061c81612ebb565b146105a8565b50845161062e81612ebb565b61063781612ebb565b151561055e565b60046040517feecfb5ec000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b5080fd5b50346101f85760803660031901126101f85767ffffffffffffffff600435818111610668576106a3903690600401612d21565b6024916024359081116108f6576106be903690600401612d21565b916106c76129e3565b916064359283151584036108f2576001600160a01b038060045416331415806108c6575b61089c578251946107c6575b5050845b838110610706578580f35b6107108183612ed9565b519061071c8187612ed9565b519187731ec2b9a77a7226acd457954820197f89b3e3a578803b1561066c578180916044604051809481936350c1d7a960e11b83528860048401528a8d8401525af180156107bb576107a3575b505060407f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd59160019482519182526020820152a1016106fb565b6107ac90612a3b565b6107b757875f610769565b8780fd5b6040513d84823e3d90fd5b86875b86811061083557509061082e929181604051936323b872dd60e01b602086015216602484015230604484015260648301526064825261080782612a9b565b7f000000000000000000000000000000000000000000000000000000000000000016613a29565b5f806106f7565b906108408286612ed9565b5189528860205260ff60408a205416156108725761086b600191610864848b612ed9565b519061307c565b91016107c9565b60046040517f237e6c28000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe9065030000000000000000000000000000000000000000000000000000000008152fd5b50807f0000000000000000000000000000000000000000000000000000000000000000163314156106eb565b5f80fd5b8380fd5b50346101f85760203660031901126101f85760043560035481101561066c5760209060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154604051908152f35b50346101f85760203660031901126101f857600435906002548210156101f85760206001600160a01b038360025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace015416604051908152f35b50346101f85760603660031901126101f85767ffffffffffffffff9060043582811161066c576109da903690600401612af1565b9160243590811161066c576109f3903690600401612d21565b916109fc6129e3565b6001600160a01b0393847f000000000000000000000000000000000000000000000000000000000000000016330361063e578251845b818110610a9457857f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb610a7987878b610a878960405195869560608752606087019061300d565b908582036020870152613049565b911660408301520390a180f35b80610ab988610aa560019489612ed9565b511686610ab28488612ed9565b51916130ed565b01610a32565b50346101f857806003193601126101f85760206040517343739b96b19ae7c2e0d80be7832325846f55fa058152f35b50346101f857806003193601126101f8576020600154604051908152f35b50346101f85760c03660031901126101f857610b266129cd565b5060603660231901126101f85767ffffffffffffffff9060843582811161066c57610b55903690600401612a0d565b60a4939193358281116108f657610b70903690600401612a0d565b9190936001600160a01b0391827f000000000000000000000000000000000000000000000000000000000000000016330361063e57860160608782031261066c5786358581116106685781610bc6918901612de3565b9460209788810135828111610cb25783610be1918301612e64565b9360408201359283116101f8575091610bfe918995949301612e64565b50610c07612fe0565b610c7e575b5050506024359182151583036108f2578492610c69575b5080610c53575b506040517f85ad8432000000000000000000000000000000000000000000000000000000008152f35b8290810103126108f257356001555f8181610c2a565b805190830151610c789161315f565b5f610c23565b82015180519081610c90575b50610c0c565b83610ca994610ca3938301019101612f66565b16613312565b835f8080610c8a565b8480fd5b50346101f85760603660031901126101f857610cd06129cd565b5060443567ffffffffffffffff811161066c57610cf1903690600401612a0d565b505060046040516262081f60e71b8152fd5b50346101f85760203660031901126101f85760043567ffffffffffffffff811161066c57610d35903690600401612d21565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e57805190825b828110610d75578380f35b610d7f8183612ed9565b51908185528460205260ff9182604087205416610da2575b506001915001610d6a565b6040516316b7a01160e01b815230600482015260248101829052731ec2b9a77a7226acd457954820197f89b3e3a578906020816044818b865af19081156110ec5788916110f7575b50610e03575060019250610dfd9061343c565b5f610d97565b602093604051948580936339f890b560e21b825285600483015260249485915afa9485156110ec5788956110b8575b50600154851015610e4a575b50505060019150610dfd565b828852876020526040882090600382015460a01c16610e6881612ebb565b80610ff1575b50507343739b96b19ae7c2e0d80be7832325846f55fa053b15610fed57869060405190637ac09bf760e01b82526060606483019185600485015283015260025480915260848201907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9084905b808210610fc8575050506003198282030160448301526020600354918281520190600384527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9084905b808210610fac57505050818084920381837343739b96b19ae7c2e0d80be7832325846f55fa055af180156107bb57610f94575b505060407f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9160019482519182526020820152a15f8080610e3e565b610f9d90612a3b565b610fa857855f610f58565b8580fd5b825484528b955060209093019260019283019290910190610f25565b82546001600160a01b031684528b955060209093019260019283019290910190610edb565b8680fd5b6005610ffb613adc565b92019061100781612ebb565b808a528160205260408a20838b526020528660408b205561102781612ebb565b6001600291828114611081575b61103d81612ebb565b14611049575b50610e6e565b808a528160205260408a20838b5260205260408a205415611043578952602052604088209088526020528660408120555f8080611043565b818c528360205260408c20858d5260205260408c20541561103457818c528360205260408c20858d526020528b6040812055611034565b9094506020813d6020116110e4575b816110d460209383612ab7565b810103126108f25751935f610e32565b3d91506110c7565b6040513d8a823e3d90fd5b611119915060203d60201161111f575b6111118183612ab7565b810190612f85565b5f610dea565b503d611107565b50346101f85760203660031901126101f8576004358015158091036108f2576001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e5760207fe13fa49eca43945f4752a5f95ae794ac03619189248561f63cc5c40d84a76bb69160045460ff60a01b1960ff60a01b8360a01b16911617600455604051908152a180f35b50346101f8576111cb36612ce1565b50505060046040516262081f60e71b8152fd5b50346101f85760603660031901126101f8576111f86129cd565b50602435801515036108f25760443567ffffffffffffffff811161066c57610cf1903690600401612a0d565b50346101f85760208060031936011261066c5760043567ffffffffffffffff811161066857611257903690600401612d21565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e57805191835b838110611297578480f35b806112a460019285612ed9565b51865285835260ff6003908160408920015460028260a892831c166112c881612ebb565b1492831561131b575b5050816112fd575b506112e5575b0161128c565b6112f86112f28286612ed9565b51613748565b6112df565b90506113098286612ed9565b5187528684526040872054165f6112d9565b8293509085929161132c868a612ed9565b518b528a885260408b200154901c1661134481612ebb565b14905f806112d1565b50346101f85761135c36612c8a565b9192936001600160a01b0390817f000000000000000000000000000000000000000000000000000000000000000016330361063e5780821691826115a15750505060ff60045460a01c16611577576113b691810190612f9d565b9281516113c281612ebb565b6113cb81612ebb565b61154f575051916113db83612ebb565b51926113e684612ebb565b6040516316b7a01160e01b81523060048201526024810183905260208160448185731ec2b9a77a7226acd457954820197f89b3e3a5785af19081156107bb578291611530575b50156115065761143b82613b5f565b61144483612ebb565b60038314610443578360406114ca9261147d7ff104cdc51204c7bbdd44ca72ad5568fe0e9c646ff1b015c4c94495b6363e61f297612ebb565b600183146114f8575b8481528060205220600160ff198254161781556114ab82600383016104af888261348d565b6114b3613adc565b6001820155600242910155604051938493846134b0565b0390a15b60206040517f343b278f000000000000000000000000000000000000000000000000000000008152f35b61150185613748565b611486565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b611549915060203d60201161111f576111118183612ab7565b5f61142c565b9291906115729450519161156283612ebb565b519261156d84612ebb565b6134d8565b6114ce565b60046040517f02dd0225000000000000000000000000000000000000000000000000000000008152fd5b92509250929316156115b7575b505050506114ce565b82845260209184835260ff600360408720015460a01c166115d781612ebb565b6115f1575050506115e8915061343c565b5f8080806115ae565b6115fc849394613374565b1561174d575081845283835260ff604085205416611667575b5061165b604084837f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d96528085522060035f918281558260018201558260028201550155565b604051908152a16115e8565b837343739b96b19ae7c2e0d80be7832325846f55fa05803b1561066c5781809160246040518094819363310bd74b60e01b83528960048401525af180156107bb57611739575b50731ec2b9a77a7226acd457954820197f89b3e3a57891823b1561066c57604051635c46a7ef60e11b81523060048201526001600160a01b0391909116602482015260448101849052608060648201525f608482015291829060a490829084905af1801561172e5715611615576117248491612a3b565b610668575f611615565b6040513d86823e3d90fd5b61174290612a3b565b6108f657835f6116ad565b929193908482528183526040822060ff19815416815560038101908573ffffffffffffffffffffffffffffffffffffffff1983541617809255600560ff611792613adc565b9360a01c169101906117a381612ebb565b80855281865260408520838652865260408520546118e0575b5050507343739b96b19ae7c2e0d80be7832325846f55fa05803b156106685782809160246040518094819363310bd74b60e01b83528b60048401525af180156118d5579083916118c1575b5050731ec2b9a77a7226acd457954820197f89b3e3a57890813b1561066857604051635c46a7ef60e11b81523060048201526001600160a01b0391909116602482015260448101869052608060648201525f6084820152908290829060a490829084905af180156107bb576118ad575b5050916040917f08e544170e8e5e6c868a252670b8aa06077b96dee38dcbb4b645909db2fb3481938351928352820152a16115e8565b6118b78291612a3b565b6101f85780611877565b6118ca90612a3b565b61066c57815f611807565b6040513d85823e3d90fd5b6118e981612ebb565b845284526040832090835283528160408120555f80806117bc565b50346101f857806003193601126101f85760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101f857806003193601126101f85760206001600160a01b0360045416604051908152f35b50346101f85760a03660031901126101f8576119896129cd565b5067ffffffffffffffff9060243582811161066c576119ac903690600401612de3565b604435838111610668576119c4903690600401612e64565b926064358181116108f6576119dd903690600401612e64565b50608435908111610668576119f6903690600401612a0d565b6001600160a01b0392837f000000000000000000000000000000000000000000000000000000000000000016330361063e57611a44918185859351916020998a96879485809401519061315f565b015180519081611b33575b50505050810103126108f25782918491356001556044604051809581937f095ea7b3000000000000000000000000000000000000000000000000000000008352731ec2b9a77a7226acd457954820197f89b3e3a57860048401525f1960248401527f0000000000000000000000000000000000000000000000000000000000000000165af1908115611b275750611b0a575b506040517f1e817d7a000000000000000000000000000000000000000000000000000000008152f35b611b2090823d841161111f576111118183612ab7565b505f611ae1565b604051903d90823e3d90fd5b83611b4694610ca3938301019101612f66565b82855f80611a4f565b50346101f857611b5e36612ce1565b60209392508101908082038481126108f6577fffffffff00000000000000000000000000000000000000000000000000000000611b9a83612f01565b167f6b10f373000000000000000000000000000000000000000000000000000000008103611fc857505090611bce91612f2e565b90506001600160a01b0391827f000000000000000000000000000000000000000000000000000000000000000016330361063e57815191815b838110611c3c5750505050505b6040517f1cff79cd000000000000000000000000000000000000000000000000000000008152f35b611c468183612ed9565b5180845283875260ff80604086205416611c65575b5050600101611c07565b6040516316b7a01160e01b815230600482015260248101839052731ec2b9a77a7226acd457954820197f89b3e3a578919089816044818a875af1908115611fa057908a9392918891611fab575b50611ccd5750505090611cc660019261343c565b905f611c5b565b604051928380936339f890b560e21b825286600483015260249485915afa928315611fa0578793611f71575b50600154831015611d11575b50505050600190611cc6565b838752868a526040872090600382015460a01c16611d2e81612ebb565b80611eb0575b50507343739b96b19ae7c2e0d80be7832325846f55fa05803b15610fed57908690604051928391637ac09bf760e01b8352606060648401918860048601528401526002548091528c8c60848501927f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9287925b828410611e8f5750505050506003198382030160448401528c600354918281520190600385528d7fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9186915b818310611e7057505050508383809203925af18015611e6557908691611e51575b50506040600193927f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9282519182528a820152a1905f8080611d05565b611e5a90612a3b565b610cb257845f611e14565b6040513d88823e3d90fd5b835485528d97508896509093019260019283019291909101908f611df3565b84541685528d97508896509093019260019283019291909101908f8f611da7565b8a60016005611ebd613adc565b940192611ec981612ebb565b808b5283835260408b20858c5283528660408c2055611ee781612ebb565b83600293848314611f3e575b5050611efe81612ebb565b14611f0a575b50611d34565b808952818c5260408920838a528c52604089205415611f045788528a526040872090875289528560408120555f8080611f04565b838d528181526040808e20888f5282528d205415611ef357838d52528d60408c2090868d52528a6040812055838e611ef3565b9092508981813d8311611f99575b611f898183612ab7565b810103126108f25751915f611cf9565b503d611f7f565b6040513d89823e3d90fd5b611fc29150843d861161111f576111118183612ab7565b5f611cb2565b7f0c5bcdda0000000000000000000000000000000000000000000000000000000081036121dc57506080136106685761200081612f01565b5061200c8482016129f9565b9167ffffffffffffffff6040830135818111610fa8578261202e918501612af1565b926060810135918211610fa857612046929101612b59565b906001600160a01b0392837f000000000000000000000000000000000000000000000000000000000000000016330361063e578381163b15610cb257846040518092636a9b8ce560e01b825286604483013060048501526040602485015287518091526064840160648260051b860101918c8a019187905b82821061214a575050505083918286920393165af1801561213f5790859161212b575b50508051935b8481106120f8575050505050611c14565b806121258561210960019486612ed9565b51168660045416606061211c8589612ed9565b510151916130ed565b016120e7565b61213490612a3b565b6108f657835f6120e1565b6040513d87823e3d90fd5b939195975093508c91955060631988820301835285518260c060a093608085820194805183528481015185840152604081015160408401526060810151606084015201519460808201528451809452019201908d905b8082106121c3575050509080600192960192019201908a95938795928b946120be565b919380600192948651815201940192018e9392916121a0565b929391927fb80a75a70000000000000000000000000000000000000000000000000000000081036123fe575060a0136101f85761221882612f01565b5067ffffffffffffffff9282850135848111610668578161223a918501612d21565b9360408401359081116106685790612253918401612d21565b9261226c6080612265606086016129f9565b9401612eae565b926001600160a01b038060045416331415806123d2575b61089c57825194612356575b5050815b8381106122a4575050505050611c14565b6122ae8183612ed9565b516122b98287612ed9565b51731ec2b9a77a7226acd457954820197f89b3e3a578803b15610fa8578580916044604051809481936350c1d7a960e11b83528860048401528760248401525af18015611e6557908691612342575b50506040600193927f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd59282519182528a820152a101612293565b61234b90612a3b565b610cb257845f612308565b83845b86811061239d575090612396929181604051936323b872dd60e01b8b86015216602484015230604484015260648301526064825261080782612a9b565b5f8061228f565b906123a88286612ed9565b51865285895260ff60408720541615610872576123cb600191610864848b612ed9565b9101612359565b50807f000000000000000000000000000000000000000000000000000000000000000016331415612283565b919392917f46ebfce600000000000000000000000000000000000000000000000000000000810361252c5750509061243591612f2e565b90506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e57805191805b83811061247c5750505050611c14565b8061248960019285612ed9565b51835282865260ff6003908160408620015460028260a892831c166124ad81612ebb565b149283156124fa575b5050816124dc575b506124ca575b0161246c565b6124d76112f28286612ed9565b6124c4565b90506124e88286612ed9565b5184528387526040842054165f6124be565b8293509085929161250b868a612ed9565b518852878b52604088200154901c1661252381612ebb565b14905f806124b6565b929391927f99df0bd500000000000000000000000000000000000000000000000000000000810361267257506080136101f85761256882612f01565b5067ffffffffffffffff9282850135848111610668578161258a918501612af1565b936040840135908111610668578392916125a79160609501612d21565b916125bc6001600160a01b03948592016129f9565b1692807f000000000000000000000000000000000000000000000000000000000000000016330361063e578451915b82811061264e5750505091612633916126407f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb9460405194859460608652606086019061300d565b9084820388860152613049565b9060408301520390a1611c14565b8061266c8361265f6001948a612ed9565b511687610ab28489612ed9565b016125eb565b92935090917f543f66a40000000000000000000000000000000000000000000000000000000003610443576040136101f8575081816126b36126ba93612f01565b5001612eae565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361063e57817fe13fa49eca43945f4752a5f95ae794ac03619189248561f63cc5c40d84a76bb691151560045460ff60a01b1960ff60a01b8360a01b16911617600455604051908152a1611c14565b50346101f85761274336612c8a565b505050505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b50346108f25760609060606003193601126108f25761278f6129cd565b67ffffffffffffffff92906024358481116108f2576127b2903690600401612af1565b936044359081116108f2576127cb903690600401612b59565b6001600160a01b0391827f000000000000000000000000000000000000000000000000000000000000000016330361063e578216803b156108f25760405190636a9b8ce560e01b825281604481013060048301526040602483015284518091526064820160648260051b840101916020808801925f905b8382106128bb57505050505091815f81819503925af180156128b05761289d575b50845192845b848110612874578580f35b80612897856128856001948b612ed9565b511686600454168561211c8589612ed9565b01612869565b6128a8919450612a3b565b5f925f612863565b6040513d5f823e3d90fd5b91939550919383906063198982030183528b8260c089519360a080820194865183528487015185840152604087015160408401528087015190830152608080960151958201528451809452019201905f905b80821061292e57505050908060019297019201920187959492939193612842565b919380600192948651815201940192018693929161290d565b346108f2575f3660031901126108f257602060ff60045460a01c166040519015158152f35b346108f257600319906060368301126108f2576129876129cd565b5060243567ffffffffffffffff928382116108f257606091360301126108f2576044359182116108f2576129c060049236908401612a0d565b50506262081f60e71b8152fd5b600435906001600160a01b03821682036108f257565b604435906001600160a01b03821682036108f257565b35906001600160a01b03821682036108f257565b9181601f840112156108f25782359167ffffffffffffffff83116108f257602083818601950101116108f257565b67ffffffffffffffff8111612a4f57604052565b634e487b7160e01b5f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117612a4f57604052565b6040810190811067ffffffffffffffff821117612a4f57604052565b60a0810190811067ffffffffffffffff821117612a4f57604052565b90601f8019910116810190811067ffffffffffffffff821117612a4f57604052565b67ffffffffffffffff8111612a4f5760051b60200190565b9080601f830112156108f2576020908235612b0b81612ad9565b93612b196040519586612ab7565b81855260208086019260051b8201019283116108f257602001905b828210612b42575050505090565b838091612b4e846129f9565b815201910190612b34565b81601f820112156108f2576020908035612b7281612ad9565b93604090612b8282519687612ab7565b828652848601918560059460051b860101948286116108f257868101935b868510612bb257505050505050505090565b67ffffffffffffffff85358181116108f25783019060a09081601f1984890301126108f25785519282840184811083821117612a4f5787528b8101358452868101358c850152606092838201358886015260809384830135908601528101359182116108f257019086603f830112156108f2578a82013591612c3383612ad9565b92612c4088519485612ab7565b808452878d8501918c1b830101918983116108f25791888e969492979593015b818110612c7a575050849550820152815201940193612ba0565b80358852968601968e9601612c60565b9060806003198301126108f2576001600160a01b039160043583811681036108f2579260243590811681036108f25791604435916064359067ffffffffffffffff82116108f257612cdd91600401612a0d565b9091565b9060406003198301126108f2576004356001600160a01b03811681036108f257916024359067ffffffffffffffff82116108f257612cdd91600401612a0d565b9080601f830112156108f2576020908235612d3b81612ad9565b93612d496040519586612ab7565b81855260208086019260051b8201019283116108f257602001905b828210612d72575050505090565b81358152908301908301612d64565b67ffffffffffffffff8111612a4f57601f01601f191660200190565b81601f820112156108f257803590612db482612d81565b92612dc26040519485612ab7565b828452602083830101116108f257815f926020809301838601378301015290565b9190916060818403126108f2576040519067ffffffffffffffff906060830182811184821017612a4f57604052829481358381116108f25781612e27918401612af1565b845260208201358381116108f25781612e41918401612d21565b602085015260408201359283116108f257604092612e5f9201612d9d565b910152565b91906040838203126108f25760405190612e7d82612a7f565b8193803560048110156108f257835260208101359167ffffffffffffffff83116108f257602092612e5f9201612d9d565b359081151582036108f257565b60041115612ec557565b634e487b7160e01b5f52602160045260245ffd5b8051821015612eed5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b35907fffffffff00000000000000000000000000000000000000000000000000000000821682036108f257565b9190916040818403126108f257612f4481612f01565b92602082013567ffffffffffffffff81116108f257612f639201612d21565b90565b908160209103126108f257516001600160a01b03811681036108f25790565b908160209103126108f2575180151581036108f25790565b9190916040818403126108f25767ffffffffffffffff9281358481116108f25781612fc9918401612e64565b9360208301359081116108f257612f639201612e64565b60443580151581036108f25790565b60643580151581036108f25790565b60843580151581036108f25790565b9081518082526020808093019301915f5b82811061302c575050505090565b83516001600160a01b03168552938101939281019260010161301e565b9081518082526020808093019301915f5b828110613068575050505090565b83518552938101939281019260010161305a565b9190820180921161308957565b634e487b7160e01b5f52601160045260245ffd5b906130a781612ebb565b7fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff75ff00000000000000000000000000000000000000000083549260a81b169116179055565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b0392909216602483015260448083019390935291815261314791613142606483612ab7565b613a29565b565b818110613154575050565b5f8155600101613149565b90815181510361044357815167ffffffffffffffff90818111612a4f5768010000000000000000808211612a4f57600254826002558083106132dc575b506020916020860160025f525f5b8281106132a1575050508351928311612a4f578211612a4f576003548260035580831061326b575b50602083019060035f525f5b83811061323957505050507f48401c66c59a655bc0d105d6c3c4c501203d6d6c4bd5774ac28592a2d5c56ecc916132346132269260405193849360408552604085019061300d565b908382036020850152613049565b0390a1565b82517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b820155918101916001016131de565b61329b90837fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9182019101613149565b5f6131d2565b81516001600160a01b03167f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace820155908401906001016131aa565b61330c90837f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9182019101613149565b5f61319c565b60206001600160a01b037f211f06c051495b535b79192c1a4531d819d569657ff4bd16daa8e9e5e6ed2bfd92168073ffffffffffffffffffffffffffffffffffffffff196004541617600455604051908152a1565b9190820391821161308957565b61337c613adc565b5f19810191818311613089575f526020915f8352604092835f20926005840160025f52808352855f20845f5283526133ca865f205460015f52828552875f20865f528552875f20549061307c565b9560025f52818452805f209260011901948511613089578460039461340b9460ff975f528152825f20549360015f528152825f20915f52525f20549061307c565b92015460a01c1661341b81612ebb565b15911590821561342a57505090565b90915081613436575090565b90501590565b60207fb26e1d7de33ad12bab10008140743de8f6a72d95d6c4d2d3b793aab536dc7add91805f525f825261348460405f2060035f918281558260018201558260028201550155565b604051908152a1565b9061349781612ebb565b60ff60a01b1960ff60a01b83549260a01b169116179055565b604091949392606082019582526134c681612ebb565b60208201526134d483612ebb565b0152565b93926134e382613b5f565b5f946134ee84612ebb565b60038414610443576134ff82612ebb565b60018214613706575b6040908151907f8fbb38ff000000000000000000000000000000000000000000000000000000008252846004830152731ec2b9a77a7226acd457954820197f89b3e3a57891602081602481865afa9081156136dd57908992915f916136e7575b5061367c575b823b1561066c578351635c46a7ef60e11b81526001600160a01b0391909116600482015230602482015260448101869052608060648201525f608482015291829060a490829084905af180156136725761365e575b50908160016132349388867fa9f990139fed927aa94ee2b7d6ccb4d0cb096c51726a86e0122ac2208c2dfe94999a5280602052208160ff198254161781556003810161360f888261348d565b613619858261309d565b42600283015573ffffffffffffffffffffffffffffffffffffffff1981541690550180541561364e575b5051938493846134b0565b613656613adc565b90555f613643565b6136688791612a3b565b610fa8575f6135c3565b82513d89823e3d90fd5b90507343739b96b19ae7c2e0d80be7832325846f55fa05803b156108f2575f8091602486518094819363310bd74b60e01b83528b60048401525af180156136dd576136ca575b50879061356e565b6136d5919850612a3b565b5f965f6136c2565b84513d5f823e3d90fd5b613700915060203d60201161111f576111118183612ab7565b5f613568565b61370f83613748565b613508565b908160409103126108f2576040519061372c82612a7f565b80519081600f0b82036108f25760209183520151602082015290565b90815f525f6020526040805f20928151635a2d1e0760e11b81526004908282820152602495731ec2b9a77a7226acd457954820197f89b3e3a5789185818981865afa908115613a1f575f916139f2575b50600260ff600384015460a81c169201805462093a809283420484830411156137c9575b5050505050505050509050565b6301dfe200908142018042116139e05785900495858702968088048714901517156139e0576137f781612ebb565b60018114806139d3575b156138a457505050505050613817904290613367565b91813b156108f257604484915f80948689519b8c96879563a4d855df60e01b87528601528401525af194851561389a577f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d949561388b575b5082519182526020820152a15b805f80808080808080806137bc565b61389490612a3b565b5f61386f565b83513d5f823e3d90fd5b806138b56002929a9896979a612ebb565b146138cc575b50505050505050505050905061387c565b6138dc6020899201938451613367565b0496808802978089048214901517156139c15787018097116139af578611613986575b5050429055813b156108f257604484915f80948689519b8c96879563a4d855df60e01b87528601528401525af194851561389a577f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d9495613977575b5082519182526020820152a1805f8080808080808080806138bb565b61398090612a3b565b5f61395b565b8192955051105f146139a55761399d904290613367565b925f806138ff565b5050505050509050565b8a601186634e487b7160e01b5f52525ffd5b8b601187634e487b7160e01b5f52525ffd5b5086602083015110613801565b8c60118a634e487b7160e01b5f52525ffd5b613a129150863d8811613a18575b613a0a8183612ab7565b810190613714565b5f613798565b503d613a00565b86513d5f823e3d90fd5b5f806001600160a01b03613a7293169360208151910182865af13d15613ad4573d90613a5482612d81565b91613a626040519384612ab7565b82523d5f602084013e5b83613c40565b8051908115159182613ab9575b5050613a885750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b613acc9250602080918301019101612f85565b155f80613a7f565b606090613a6c565b6040517f060406180000000000000000000000000000000000000000000000000000000081526020816004817343739b96b19ae7c2e0d80be7832325846f55fa055afa9081156128b0575f91613b30575090565b90506020813d602011613b57575b81613b4b60209383612ab7565b810103126108f2575190565b3d9150613b3e565b604090815190635a2d1e0760e11b825260048201528181602481731ec2b9a77a7226acd457954820197f89b3e3a5785afa908115613c36575f91613c19575b50602081015162093a80420190814211613089571115613bf05751600f0b60015411613bc75750565b600490517f2566c439000000000000000000000000000000000000000000000000000000008152fd5b600482517f0cc5b598000000000000000000000000000000000000000000000000000000008152fd5b613c309150823d8411613a1857613a0a8183612ab7565b5f613b9e565b82513d5f823e3d90fd5b90613c7f5750805115613c5557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580613cca575b613c90575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613c8856fea164736f6c6343000818000a0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a40000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf85265
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f905f3560e01c908163015522201461296c5750806302befd24146129475780630c5bcdda14612772578063150b7a02146127345780631cff79cd14611b4f5780631e817d7a1461196f5780632b3297f9146119485780632fc0945c14611904578063343b278f1461134d57806346ebfce6146112245780634f195105146111de578063526e0705146111bc578063543f66a4146111265780636b10f37314610d03578063827e0eb214610cb657806385ad843214610b0c5780638e5ac18d14610aee5780638ebf2fd614610abf57806399df0bd5146109a6578063b05391871461094b578063b5f163ff146108fa578063b80a75a714610670578063c027774f1461031c578063c863657d146102ed578063cbcbecb9146102a9578063dd5d55fe14610224578063e8868a79146101fb5763fd6bfc5614610152575f80fd5b346101f857806003193601126101f8578060c09160a060405161017481612a63565b828152826020820152826040820152826060820152826080820152015260405161019d81612a63565b8181526020810190600182526040810160018152606082019084825260a060808401938685520193858552604051958652511515602086015251151560408501525115156060840152511515608083015251151560a0820152f35b80fd5b50346101f85760203660031901126101f857602061021a600435613374565b6040519015158152f35b50346101f85760203660031901126101f857604060c0916004358152806020522060ff815416906001810154906001600160a01b036003600283015492015460ff8160a01c169260ff8260a81c169460405196151587526020870152604086015216606084015261029481612ebb565b60808301526102a281612ebb565b60a0820152f35b50346101f857806003193601126101f85760206040516001600160a01b037f000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf85265168152f35b50346101f857806003193601126101f8576020604051731ec2b9a77a7226acd457954820197f89b3e3a5788152f35b50346101f85760e03660031901126101f8576103366129cd565b6024359190606036604319011261066c5767ffffffffffffffff9160a43583811161066c57610369903690600401612a0d565b9360c43590811161066857610382903690600401612a0d565b50506001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57604082866103cb9452806020522093810190612f9d565b91600382516103d981612ebb565b6103e281612ebb565b14610443576103ef612fef565b610540575b50506103fe612ffe565b610473575b50505061040e612fe0565b8015610454575b6104435760206040517fc027774f000000000000000000000000000000000000000000000000000000008152f35b600460405163b4fa3fb360e01b8152fd5b5061045d612ffe565b158015610415575061046d612fef565b15610415565b600382019160ff835460a81c1661048981612ebb565b600181141580610523575b156104c15750506104b992916104b49151906104af82612ebb565b61309d565b613748565b5f8080610403565b60029194506104cf81612ebb565b141580610506575b156104f6576104f19260024291015551906104af82612ebb565b6104b9565b6104f1925051906104af82612ebb565b506002815161051481612ebb565b61051d81612ebb565b146104d7565b506001835161053181612ebb565b61053a81612ebb565b14610494565b600384018560ff825460a01c1661055681612ebb565b801580610622575b15610590575060ff610589946105738361343c565b519261057e84612ebb565b5460a81c16926134d8565b5f806103f4565b939250505061059e82612ebb565b6002821480610605575b1591826105c8575b50501561058957600460405163b4fa3fb360e01b8152fd5b60019192506105d681612ebb565b1490816105e7575b50155f806105b0565b60029150516105f581612ebb565b6105fe81612ebb565b145f6105de565b506001815161061381612ebb565b61061c81612ebb565b146105a8565b50845161062e81612ebb565b61063781612ebb565b151561055e565b60046040517feecfb5ec000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b5080fd5b50346101f85760803660031901126101f85767ffffffffffffffff600435818111610668576106a3903690600401612d21565b6024916024359081116108f6576106be903690600401612d21565b916106c76129e3565b916064359283151584036108f2576001600160a01b038060045416331415806108c6575b61089c578251946107c6575b5050845b838110610706578580f35b6107108183612ed9565b519061071c8187612ed9565b519187731ec2b9a77a7226acd457954820197f89b3e3a578803b1561066c578180916044604051809481936350c1d7a960e11b83528860048401528a8d8401525af180156107bb576107a3575b505060407f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd59160019482519182526020820152a1016106fb565b6107ac90612a3b565b6107b757875f610769565b8780fd5b6040513d84823e3d90fd5b86875b86811061083557509061082e929181604051936323b872dd60e01b602086015216602484015230604484015260648301526064825261080782612a9b565b7f000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf8526516613a29565b5f806106f7565b906108408286612ed9565b5189528860205260ff60408a205416156108725761086b600191610864848b612ed9565b519061307c565b91016107c9565b60046040517f237e6c28000000000000000000000000000000000000000000000000000000008152fd5b60046040517fe9065030000000000000000000000000000000000000000000000000000000008152fd5b50807f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a40163314156106eb565b5f80fd5b8380fd5b50346101f85760203660031901126101f85760043560035481101561066c5760209060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0154604051908152f35b50346101f85760203660031901126101f857600435906002548210156101f85760206001600160a01b038360025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace015416604051908152f35b50346101f85760603660031901126101f85767ffffffffffffffff9060043582811161066c576109da903690600401612af1565b9160243590811161066c576109f3903690600401612d21565b916109fc6129e3565b6001600160a01b0393847f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e578251845b818110610a9457857f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb610a7987878b610a878960405195869560608752606087019061300d565b908582036020870152613049565b911660408301520390a180f35b80610ab988610aa560019489612ed9565b511686610ab28488612ed9565b51916130ed565b01610a32565b50346101f857806003193601126101f85760206040517343739b96b19ae7c2e0d80be7832325846f55fa058152f35b50346101f857806003193601126101f8576020600154604051908152f35b50346101f85760c03660031901126101f857610b266129cd565b5060603660231901126101f85767ffffffffffffffff9060843582811161066c57610b55903690600401612a0d565b60a4939193358281116108f657610b70903690600401612a0d565b9190936001600160a01b0391827f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57860160608782031261066c5786358581116106685781610bc6918901612de3565b9460209788810135828111610cb25783610be1918301612e64565b9360408201359283116101f8575091610bfe918995949301612e64565b50610c07612fe0565b610c7e575b5050506024359182151583036108f2578492610c69575b5080610c53575b506040517f85ad8432000000000000000000000000000000000000000000000000000000008152f35b8290810103126108f257356001555f8181610c2a565b805190830151610c789161315f565b5f610c23565b82015180519081610c90575b50610c0c565b83610ca994610ca3938301019101612f66565b16613312565b835f8080610c8a565b8480fd5b50346101f85760603660031901126101f857610cd06129cd565b5060443567ffffffffffffffff811161066c57610cf1903690600401612a0d565b505060046040516262081f60e71b8152fd5b50346101f85760203660031901126101f85760043567ffffffffffffffff811161066c57610d35903690600401612d21565b6001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57805190825b828110610d75578380f35b610d7f8183612ed9565b51908185528460205260ff9182604087205416610da2575b506001915001610d6a565b6040516316b7a01160e01b815230600482015260248101829052731ec2b9a77a7226acd457954820197f89b3e3a578906020816044818b865af19081156110ec5788916110f7575b50610e03575060019250610dfd9061343c565b5f610d97565b602093604051948580936339f890b560e21b825285600483015260249485915afa9485156110ec5788956110b8575b50600154851015610e4a575b50505060019150610dfd565b828852876020526040882090600382015460a01c16610e6881612ebb565b80610ff1575b50507343739b96b19ae7c2e0d80be7832325846f55fa053b15610fed57869060405190637ac09bf760e01b82526060606483019185600485015283015260025480915260848201907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9084905b808210610fc8575050506003198282030160448301526020600354918281520190600384527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9084905b808210610fac57505050818084920381837343739b96b19ae7c2e0d80be7832325846f55fa055af180156107bb57610f94575b505060407f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9160019482519182526020820152a15f8080610e3e565b610f9d90612a3b565b610fa857855f610f58565b8580fd5b825484528b955060209093019260019283019290910190610f25565b82546001600160a01b031684528b955060209093019260019283019290910190610edb565b8680fd5b6005610ffb613adc565b92019061100781612ebb565b808a528160205260408a20838b526020528660408b205561102781612ebb565b6001600291828114611081575b61103d81612ebb565b14611049575b50610e6e565b808a528160205260408a20838b5260205260408a205415611043578952602052604088209088526020528660408120555f8080611043565b818c528360205260408c20858d5260205260408c20541561103457818c528360205260408c20858d526020528b6040812055611034565b9094506020813d6020116110e4575b816110d460209383612ab7565b810103126108f25751935f610e32565b3d91506110c7565b6040513d8a823e3d90fd5b611119915060203d60201161111f575b6111118183612ab7565b810190612f85565b5f610dea565b503d611107565b50346101f85760203660031901126101f8576004358015158091036108f2576001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e5760207fe13fa49eca43945f4752a5f95ae794ac03619189248561f63cc5c40d84a76bb69160045460ff60a01b1960ff60a01b8360a01b16911617600455604051908152a180f35b50346101f8576111cb36612ce1565b50505060046040516262081f60e71b8152fd5b50346101f85760603660031901126101f8576111f86129cd565b50602435801515036108f25760443567ffffffffffffffff811161066c57610cf1903690600401612a0d565b50346101f85760208060031936011261066c5760043567ffffffffffffffff811161066857611257903690600401612d21565b6001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57805191835b838110611297578480f35b806112a460019285612ed9565b51865285835260ff6003908160408920015460028260a892831c166112c881612ebb565b1492831561131b575b5050816112fd575b506112e5575b0161128c565b6112f86112f28286612ed9565b51613748565b6112df565b90506113098286612ed9565b5187528684526040872054165f6112d9565b8293509085929161132c868a612ed9565b518b528a885260408b200154901c1661134481612ebb565b14905f806112d1565b50346101f85761135c36612c8a565b9192936001600160a01b0390817f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e5780821691826115a15750505060ff60045460a01c16611577576113b691810190612f9d565b9281516113c281612ebb565b6113cb81612ebb565b61154f575051916113db83612ebb565b51926113e684612ebb565b6040516316b7a01160e01b81523060048201526024810183905260208160448185731ec2b9a77a7226acd457954820197f89b3e3a5785af19081156107bb578291611530575b50156115065761143b82613b5f565b61144483612ebb565b60038314610443578360406114ca9261147d7ff104cdc51204c7bbdd44ca72ad5568fe0e9c646ff1b015c4c94495b6363e61f297612ebb565b600183146114f8575b8481528060205220600160ff198254161781556114ab82600383016104af888261348d565b6114b3613adc565b6001820155600242910155604051938493846134b0565b0390a15b60206040517f343b278f000000000000000000000000000000000000000000000000000000008152f35b61150185613748565b611486565b60046040517fe433766c000000000000000000000000000000000000000000000000000000008152fd5b611549915060203d60201161111f576111118183612ab7565b5f61142c565b9291906115729450519161156283612ebb565b519261156d84612ebb565b6134d8565b6114ce565b60046040517f02dd0225000000000000000000000000000000000000000000000000000000008152fd5b92509250929316156115b7575b505050506114ce565b82845260209184835260ff600360408720015460a01c166115d781612ebb565b6115f1575050506115e8915061343c565b5f8080806115ae565b6115fc849394613374565b1561174d575081845283835260ff604085205416611667575b5061165b604084837f5b6b431d4476a211bb7d41c20d1aab9ae2321deee0d20be3d9fc9b1093fa6e3d96528085522060035f918281558260018201558260028201550155565b604051908152a16115e8565b837343739b96b19ae7c2e0d80be7832325846f55fa05803b1561066c5781809160246040518094819363310bd74b60e01b83528960048401525af180156107bb57611739575b50731ec2b9a77a7226acd457954820197f89b3e3a57891823b1561066c57604051635c46a7ef60e11b81523060048201526001600160a01b0391909116602482015260448101849052608060648201525f608482015291829060a490829084905af1801561172e5715611615576117248491612a3b565b610668575f611615565b6040513d86823e3d90fd5b61174290612a3b565b6108f657835f6116ad565b929193908482528183526040822060ff19815416815560038101908573ffffffffffffffffffffffffffffffffffffffff1983541617809255600560ff611792613adc565b9360a01c169101906117a381612ebb565b80855281865260408520838652865260408520546118e0575b5050507343739b96b19ae7c2e0d80be7832325846f55fa05803b156106685782809160246040518094819363310bd74b60e01b83528b60048401525af180156118d5579083916118c1575b5050731ec2b9a77a7226acd457954820197f89b3e3a57890813b1561066857604051635c46a7ef60e11b81523060048201526001600160a01b0391909116602482015260448101869052608060648201525f6084820152908290829060a490829084905af180156107bb576118ad575b5050916040917f08e544170e8e5e6c868a252670b8aa06077b96dee38dcbb4b645909db2fb3481938351928352820152a16115e8565b6118b78291612a3b565b6101f85780611877565b6118ca90612a3b565b61066c57815f611807565b6040513d85823e3d90fd5b6118e981612ebb565b845284526040832090835283528160408120555f80806117bc565b50346101f857806003193601126101f85760206040516001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a40168152f35b50346101f857806003193601126101f85760206001600160a01b0360045416604051908152f35b50346101f85760a03660031901126101f8576119896129cd565b5067ffffffffffffffff9060243582811161066c576119ac903690600401612de3565b604435838111610668576119c4903690600401612e64565b926064358181116108f6576119dd903690600401612e64565b50608435908111610668576119f6903690600401612a0d565b6001600160a01b0392837f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57611a44918185859351916020998a96879485809401519061315f565b015180519081611b33575b50505050810103126108f25782918491356001556044604051809581937f095ea7b3000000000000000000000000000000000000000000000000000000008352731ec2b9a77a7226acd457954820197f89b3e3a57860048401525f1960248401527f000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf85265165af1908115611b275750611b0a575b506040517f1e817d7a000000000000000000000000000000000000000000000000000000008152f35b611b2090823d841161111f576111118183612ab7565b505f611ae1565b604051903d90823e3d90fd5b83611b4694610ca3938301019101612f66565b82855f80611a4f565b50346101f857611b5e36612ce1565b60209392508101908082038481126108f6577fffffffff00000000000000000000000000000000000000000000000000000000611b9a83612f01565b167f6b10f373000000000000000000000000000000000000000000000000000000008103611fc857505090611bce91612f2e565b90506001600160a01b0391827f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57815191815b838110611c3c5750505050505b6040517f1cff79cd000000000000000000000000000000000000000000000000000000008152f35b611c468183612ed9565b5180845283875260ff80604086205416611c65575b5050600101611c07565b6040516316b7a01160e01b815230600482015260248101839052731ec2b9a77a7226acd457954820197f89b3e3a578919089816044818a875af1908115611fa057908a9392918891611fab575b50611ccd5750505090611cc660019261343c565b905f611c5b565b604051928380936339f890b560e21b825286600483015260249485915afa928315611fa0578793611f71575b50600154831015611d11575b50505050600190611cc6565b838752868a526040872090600382015460a01c16611d2e81612ebb565b80611eb0575b50507343739b96b19ae7c2e0d80be7832325846f55fa05803b15610fed57908690604051928391637ac09bf760e01b8352606060648401918860048601528401526002548091528c8c60848501927f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9287925b828410611e8f5750505050506003198382030160448401528c600354918281520190600385528d7fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9186915b818310611e7057505050508383809203925af18015611e6557908691611e51575b50506040600193927f33952ef907843fd2ddd118a92dd935debf65b72965a557a364ea08deffca032f9282519182528a820152a1905f8080611d05565b611e5a90612a3b565b610cb257845f611e14565b6040513d88823e3d90fd5b835485528d97508896509093019260019283019291909101908f611df3565b84541685528d97508896509093019260019283019291909101908f8f611da7565b8a60016005611ebd613adc565b940192611ec981612ebb565b808b5283835260408b20858c5283528660408c2055611ee781612ebb565b83600293848314611f3e575b5050611efe81612ebb565b14611f0a575b50611d34565b808952818c5260408920838a528c52604089205415611f045788528a526040872090875289528560408120555f8080611f04565b838d528181526040808e20888f5282528d205415611ef357838d52528d60408c2090868d52528a6040812055838e611ef3565b9092508981813d8311611f99575b611f898183612ab7565b810103126108f25751915f611cf9565b503d611f7f565b6040513d89823e3d90fd5b611fc29150843d861161111f576111118183612ab7565b5f611cb2565b7f0c5bcdda0000000000000000000000000000000000000000000000000000000081036121dc57506080136106685761200081612f01565b5061200c8482016129f9565b9167ffffffffffffffff6040830135818111610fa8578261202e918501612af1565b926060810135918211610fa857612046929101612b59565b906001600160a01b0392837f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e578381163b15610cb257846040518092636a9b8ce560e01b825286604483013060048501526040602485015287518091526064840160648260051b860101918c8a019187905b82821061214a575050505083918286920393165af1801561213f5790859161212b575b50508051935b8481106120f8575050505050611c14565b806121258561210960019486612ed9565b51168660045416606061211c8589612ed9565b510151916130ed565b016120e7565b61213490612a3b565b6108f657835f6120e1565b6040513d87823e3d90fd5b939195975093508c91955060631988820301835285518260c060a093608085820194805183528481015185840152604081015160408401526060810151606084015201519460808201528451809452019201908d905b8082106121c3575050509080600192960192019201908a95938795928b946120be565b919380600192948651815201940192018e9392916121a0565b929391927fb80a75a70000000000000000000000000000000000000000000000000000000081036123fe575060a0136101f85761221882612f01565b5067ffffffffffffffff9282850135848111610668578161223a918501612d21565b9360408401359081116106685790612253918401612d21565b9261226c6080612265606086016129f9565b9401612eae565b926001600160a01b038060045416331415806123d2575b61089c57825194612356575b5050815b8381106122a4575050505050611c14565b6122ae8183612ed9565b516122b98287612ed9565b51731ec2b9a77a7226acd457954820197f89b3e3a578803b15610fa8578580916044604051809481936350c1d7a960e11b83528860048401528760248401525af18015611e6557908691612342575b50506040600193927f61ab42f4ca689a8cdc8b5d584f8f3b6b6c10c5959daa531cc84386589b9bccd59282519182528a820152a101612293565b61234b90612a3b565b610cb257845f612308565b83845b86811061239d575090612396929181604051936323b872dd60e01b8b86015216602484015230604484015260648301526064825261080782612a9b565b5f8061228f565b906123a88286612ed9565b51865285895260ff60408720541615610872576123cb600191610864848b612ed9565b9101612359565b50807f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016331415612283565b919392917f46ebfce600000000000000000000000000000000000000000000000000000000810361252c5750509061243591612f2e565b90506001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57805191805b83811061247c5750505050611c14565b8061248960019285612ed9565b51835282865260ff6003908160408620015460028260a892831c166124ad81612ebb565b149283156124fa575b5050816124dc575b506124ca575b0161246c565b6124d76112f28286612ed9565b6124c4565b90506124e88286612ed9565b5184528387526040842054165f6124be565b8293509085929161250b868a612ed9565b518852878b52604088200154901c1661252381612ebb565b14905f806124b6565b929391927f99df0bd500000000000000000000000000000000000000000000000000000000810361267257506080136101f85761256882612f01565b5067ffffffffffffffff9282850135848111610668578161258a918501612af1565b936040840135908111610668578392916125a79160609501612d21565b916125bc6001600160a01b03948592016129f9565b1692807f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e578451915b82811061264e5750505091612633916126407f770746e85ebd8efe8367457cb56498310d53aa9c0ed8005e1565137c963d55eb9460405194859460608652606086019061300d565b9084820388860152613049565b9060408301520390a1611c14565b8061266c8361265f6001948a612ed9565b511687610ab28489612ed9565b016125eb565b92935090917f543f66a40000000000000000000000000000000000000000000000000000000003610443576040136101f8575081816126b36126ba93612f01565b5001612eae565b6001600160a01b037f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e57817fe13fa49eca43945f4752a5f95ae794ac03619189248561f63cc5c40d84a76bb691151560045460ff60a01b1960ff60a01b8360a01b16911617600455604051908152a1611c14565b50346101f85761274336612c8a565b505050505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b50346108f25760609060606003193601126108f25761278f6129cd565b67ffffffffffffffff92906024358481116108f2576127b2903690600401612af1565b936044359081116108f2576127cb903690600401612b59565b6001600160a01b0391827f0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a4016330361063e578216803b156108f25760405190636a9b8ce560e01b825281604481013060048301526040602483015284518091526064820160648260051b840101916020808801925f905b8382106128bb57505050505091815f81819503925af180156128b05761289d575b50845192845b848110612874578580f35b80612897856128856001948b612ed9565b511686600454168561211c8589612ed9565b01612869565b6128a8919450612a3b565b5f925f612863565b6040513d5f823e3d90fd5b91939550919383906063198982030183528b8260c089519360a080820194865183528487015185840152604087015160408401528087015190830152608080960151958201528451809452019201905f905b80821061292e57505050908060019297019201920187959492939193612842565b919380600192948651815201940192018693929161290d565b346108f2575f3660031901126108f257602060ff60045460a01c166040519015158152f35b346108f257600319906060368301126108f2576129876129cd565b5060243567ffffffffffffffff928382116108f257606091360301126108f2576044359182116108f2576129c060049236908401612a0d565b50506262081f60e71b8152fd5b600435906001600160a01b03821682036108f257565b604435906001600160a01b03821682036108f257565b35906001600160a01b03821682036108f257565b9181601f840112156108f25782359167ffffffffffffffff83116108f257602083818601950101116108f257565b67ffffffffffffffff8111612a4f57604052565b634e487b7160e01b5f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117612a4f57604052565b6040810190811067ffffffffffffffff821117612a4f57604052565b60a0810190811067ffffffffffffffff821117612a4f57604052565b90601f8019910116810190811067ffffffffffffffff821117612a4f57604052565b67ffffffffffffffff8111612a4f5760051b60200190565b9080601f830112156108f2576020908235612b0b81612ad9565b93612b196040519586612ab7565b81855260208086019260051b8201019283116108f257602001905b828210612b42575050505090565b838091612b4e846129f9565b815201910190612b34565b81601f820112156108f2576020908035612b7281612ad9565b93604090612b8282519687612ab7565b828652848601918560059460051b860101948286116108f257868101935b868510612bb257505050505050505090565b67ffffffffffffffff85358181116108f25783019060a09081601f1984890301126108f25785519282840184811083821117612a4f5787528b8101358452868101358c850152606092838201358886015260809384830135908601528101359182116108f257019086603f830112156108f2578a82013591612c3383612ad9565b92612c4088519485612ab7565b808452878d8501918c1b830101918983116108f25791888e969492979593015b818110612c7a575050849550820152815201940193612ba0565b80358852968601968e9601612c60565b9060806003198301126108f2576001600160a01b039160043583811681036108f2579260243590811681036108f25791604435916064359067ffffffffffffffff82116108f257612cdd91600401612a0d565b9091565b9060406003198301126108f2576004356001600160a01b03811681036108f257916024359067ffffffffffffffff82116108f257612cdd91600401612a0d565b9080601f830112156108f2576020908235612d3b81612ad9565b93612d496040519586612ab7565b81855260208086019260051b8201019283116108f257602001905b828210612d72575050505090565b81358152908301908301612d64565b67ffffffffffffffff8111612a4f57601f01601f191660200190565b81601f820112156108f257803590612db482612d81565b92612dc26040519485612ab7565b828452602083830101116108f257815f926020809301838601378301015290565b9190916060818403126108f2576040519067ffffffffffffffff906060830182811184821017612a4f57604052829481358381116108f25781612e27918401612af1565b845260208201358381116108f25781612e41918401612d21565b602085015260408201359283116108f257604092612e5f9201612d9d565b910152565b91906040838203126108f25760405190612e7d82612a7f565b8193803560048110156108f257835260208101359167ffffffffffffffff83116108f257602092612e5f9201612d9d565b359081151582036108f257565b60041115612ec557565b634e487b7160e01b5f52602160045260245ffd5b8051821015612eed5760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b35907fffffffff00000000000000000000000000000000000000000000000000000000821682036108f257565b9190916040818403126108f257612f4481612f01565b92602082013567ffffffffffffffff81116108f257612f639201612d21565b90565b908160209103126108f257516001600160a01b03811681036108f25790565b908160209103126108f2575180151581036108f25790565b9190916040818403126108f25767ffffffffffffffff9281358481116108f25781612fc9918401612e64565b9360208301359081116108f257612f639201612e64565b60443580151581036108f25790565b60643580151581036108f25790565b60843580151581036108f25790565b9081518082526020808093019301915f5b82811061302c575050505090565b83516001600160a01b03168552938101939281019260010161301e565b9081518082526020808093019301915f5b828110613068575050505090565b83518552938101939281019260010161305a565b9190820180921161308957565b634e487b7160e01b5f52601160045260245ffd5b906130a781612ebb565b7fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff75ff00000000000000000000000000000000000000000083549260a81b169116179055565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b0392909216602483015260448083019390935291815261314791613142606483612ab7565b613a29565b565b818110613154575050565b5f8155600101613149565b90815181510361044357815167ffffffffffffffff90818111612a4f5768010000000000000000808211612a4f57600254826002558083106132dc575b506020916020860160025f525f5b8281106132a1575050508351928311612a4f578211612a4f576003548260035580831061326b575b50602083019060035f525f5b83811061323957505050507f48401c66c59a655bc0d105d6c3c4c501203d6d6c4bd5774ac28592a2d5c56ecc916132346132269260405193849360408552604085019061300d565b908382036020850152613049565b0390a1565b82517fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b820155918101916001016131de565b61329b90837fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b9182019101613149565b5f6131d2565b81516001600160a01b03167f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace820155908401906001016131aa565b61330c90837f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9182019101613149565b5f61319c565b60206001600160a01b037f211f06c051495b535b79192c1a4531d819d569657ff4bd16daa8e9e5e6ed2bfd92168073ffffffffffffffffffffffffffffffffffffffff196004541617600455604051908152a1565b9190820391821161308957565b61337c613adc565b5f19810191818311613089575f526020915f8352604092835f20926005840160025f52808352855f20845f5283526133ca865f205460015f52828552875f20865f528552875f20549061307c565b9560025f52818452805f209260011901948511613089578460039461340b9460ff975f528152825f20549360015f528152825f20915f52525f20549061307c565b92015460a01c1661341b81612ebb565b15911590821561342a57505090565b90915081613436575090565b90501590565b60207fb26e1d7de33ad12bab10008140743de8f6a72d95d6c4d2d3b793aab536dc7add91805f525f825261348460405f2060035f918281558260018201558260028201550155565b604051908152a1565b9061349781612ebb565b60ff60a01b1960ff60a01b83549260a01b169116179055565b604091949392606082019582526134c681612ebb565b60208201526134d483612ebb565b0152565b93926134e382613b5f565b5f946134ee84612ebb565b60038414610443576134ff82612ebb565b60018214613706575b6040908151907f8fbb38ff000000000000000000000000000000000000000000000000000000008252846004830152731ec2b9a77a7226acd457954820197f89b3e3a57891602081602481865afa9081156136dd57908992915f916136e7575b5061367c575b823b1561066c578351635c46a7ef60e11b81526001600160a01b0391909116600482015230602482015260448101869052608060648201525f608482015291829060a490829084905af180156136725761365e575b50908160016132349388867fa9f990139fed927aa94ee2b7d6ccb4d0cb096c51726a86e0122ac2208c2dfe94999a5280602052208160ff198254161781556003810161360f888261348d565b613619858261309d565b42600283015573ffffffffffffffffffffffffffffffffffffffff1981541690550180541561364e575b5051938493846134b0565b613656613adc565b90555f613643565b6136688791612a3b565b610fa8575f6135c3565b82513d89823e3d90fd5b90507343739b96b19ae7c2e0d80be7832325846f55fa05803b156108f2575f8091602486518094819363310bd74b60e01b83528b60048401525af180156136dd576136ca575b50879061356e565b6136d5919850612a3b565b5f965f6136c2565b84513d5f823e3d90fd5b613700915060203d60201161111f576111118183612ab7565b5f613568565b61370f83613748565b613508565b908160409103126108f2576040519061372c82612a7f565b80519081600f0b82036108f25760209183520151602082015290565b90815f525f6020526040805f20928151635a2d1e0760e11b81526004908282820152602495731ec2b9a77a7226acd457954820197f89b3e3a5789185818981865afa908115613a1f575f916139f2575b50600260ff600384015460a81c169201805462093a809283420484830411156137c9575b5050505050505050509050565b6301dfe200908142018042116139e05785900495858702968088048714901517156139e0576137f781612ebb565b60018114806139d3575b156138a457505050505050613817904290613367565b91813b156108f257604484915f80948689519b8c96879563a4d855df60e01b87528601528401525af194851561389a577f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d949561388b575b5082519182526020820152a15b805f80808080808080806137bc565b61389490612a3b565b5f61386f565b83513d5f823e3d90fd5b806138b56002929a9896979a612ebb565b146138cc575b50505050505050505050905061387c565b6138dc6020899201938451613367565b0496808802978089048214901517156139c15787018097116139af578611613986575b5050429055813b156108f257604484915f80948689519b8c96879563a4d855df60e01b87528601528401525af194851561389a577f43cea24d3d066f77347a713abc68986d484cb771c1b6d8ffb6a63eb557ab593d9495613977575b5082519182526020820152a1805f8080808080808080806138bb565b61398090612a3b565b5f61395b565b8192955051105f146139a55761399d904290613367565b925f806138ff565b5050505050509050565b8a601186634e487b7160e01b5f52525ffd5b8b601187634e487b7160e01b5f52525ffd5b5086602083015110613801565b8c60118a634e487b7160e01b5f52525ffd5b613a129150863d8811613a18575b613a0a8183612ab7565b810190613714565b5f613798565b503d613a00565b86513d5f823e3d90fd5b5f806001600160a01b03613a7293169360208151910182865af13d15613ad4573d90613a5482612d81565b91613a626040519384612ab7565b82523d5f602084013e5b83613c40565b8051908115159182613ab9575b5050613a885750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b613acc9250602080918301019101612f85565b155f80613a7f565b606090613a6c565b6040517f060406180000000000000000000000000000000000000000000000000000000081526020816004817343739b96b19ae7c2e0d80be7832325846f55fa055afa9081156128b0575f91613b30575090565b90506020813d602011613b57575b81613b4b60209383612ab7565b810103126108f2575190565b3d9150613b3e565b604090815190635a2d1e0760e11b825260048201528181602481731ec2b9a77a7226acd457954820197f89b3e3a5785afa908115613c36575f91613c19575b50602081015162093a80420190814211613089571115613bf05751600f0b60015411613bc75750565b600490517f2566c439000000000000000000000000000000000000000000000000000000008152fd5b600482517f0cc5b598000000000000000000000000000000000000000000000000000000008152fd5b613c309150823d8411613a1857613a0a8183612ab7565b5f613b9e565b82513d5f823e3d90fd5b90613c7f5750805115613c5557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580613cca575b613c90575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613c8856fea164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a40000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf85265
-----Decoded View---------------
Arg [0] : _puppeteer (address): 0x8a725d6D36bBD09feCC5bD9a37eb7fCa34895A40
Arg [1] : initialTHE (address): 0x455d5f11Fea33A8fa9D3e285930b478B6bF85265
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000008a725d6d36bbd09fecc5bd9a37eb7fca34895a40
Arg [1] : 000000000000000000000000455d5f11fea33a8fa9d3e285930b478b6bf85265
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.