Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
VoterUpgradeableV2
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {SafeERC20Upgradeable, IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import {IAlgebraFactory} from "@cryptoalgebra/integral-core/contracts/interfaces/IAlgebraFactory.sol";
import {BlastGovernorClaimableSetup} from "../integration/BlastGovernorClaimableSetup.sol";
import {IPairIntegrationInfo} from "../integration/interfaces/IPairIntegrationInfo.sol";
import {IVotingEscrow} from "./interfaces/IVotingEscrow.sol";
import {IPairFactory} from "../dexV2/interfaces/IPairFactory.sol";
import {IGaugeFactory} from "../gauges/interfaces/IGaugeFactory.sol";
import {IBribeFactory} from "../bribes/interfaces/IBribeFactory.sol";
import {IMinter} from "./interfaces/IMinter.sol";
import {IVeFnxSplitMerklAidrop} from "./interfaces/IVeFnxSplitMerklAidrop.sol";
import {IMerklDistributor} from "../integration/interfaces/IMerklDistributor.sol";
import {IManagedNFTManager} from "../nest/interfaces/IManagedNFTManager.sol";
import {IBribe} from "../bribes/interfaces/IBribe.sol";
import {IGauge} from "../gauges/interfaces/IGauge.sol";
import "./libraries/LibVoterErrors.sol";
import "./interfaces/IVoter.sol";
/**
* @title VoterUpgradeableV2
* @notice This contract manages the voting process within a decentralized protocol,
* integrating gauges, bribes, and NFT-based voting mechanisms.
* @dev The contract is upgradeable and includes access control, reentrancy protection,
* and voting delay mechanisms.
* @custom:security ReentrancyGuardUpgradeable to prevent reentrancy attacks.
* @custom:security AccessControlUpgradeable for role-based access control.
*/
contract VoterUpgradeableV2 is IVoter, AccessControlUpgradeable, BlastGovernorClaimableSetup, ReentrancyGuardUpgradeable {
using SafeERC20Upgradeable for IERC20Upgradeable;
/// @notice Role identifier for governance operations.
bytes32 internal constant _GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
/// @notice Role identifier for voter administration.
bytes32 internal constant _VOTER_ADMIN_ROLE = keccak256("VOTER_ADMIN_ROLE");
/// @notice Number of seconds in one week (604800 seconds).
uint256 internal constant _WEEK = 604800;
/// @notice Address of the Voting Escrow contract.
address public votingEscrow;
/// @notice Address of the main ERC20 token used in the protocol.
address public token;
/// @notice Address of the Minter contract.
address public minter;
/// @notice Address of the Bribe Factory contract.
address public bribeFactory;
/// @notice Address of the V2 Pool Factory contract.
address public v2PoolFactory;
/// @notice Address of the V3 Pool Factory contract.
address public v3PoolFactory;
/// @notice Address of the V2 Gauge Factory contract.
address public v2GaugeFactory;
/// @notice Address of the V3 Gauge Factory contract.
address public v3GaugeFactory;
/// @notice Address of the Merkl Distributor contract.
address public merklDistributor;
/// @notice Address of the veFNX Merkl Airdrop contract.
address public veFnxMerklAidrop;
/// @notice Address of the Managed NFT Manager contract.
address public managedNFTManager;
/// @notice Array of pool addresses managed by the contract.
address[] public pools;
/// @notice Array of V2 pool addresses.
address[] public v2Pools;
/// @notice Array of V3 pool addresses.
address[] public v3Pools;
/// @notice Current index used in reward distribution calculations.
uint256 public index;
/// @notice Delay period before a vote can be cast again.
uint256 public voteDelay;
/// @notice Duration of the distribution window, in seconds.
uint256 public distributionWindowDuration;
/// @notice Mapping of pool addresses to their corresponding gauge addresses.
mapping(address pool => address) public poolToGauge;
/// @notice Mapping of gauge addresses to their corresponding state.
mapping(address gauge => GaugeState) public gaugesState;
/// @notice Mapping of NFT token IDs to the pools they have voted for.
mapping(uint256 tokenId => address[]) public poolVote;
/// @notice Mapping of NFT token IDs to the last time they voted.
mapping(uint256 tokenId => uint256) public lastVotedTimestamps;
/// @notice Mapping of NFT token IDs to their votes per pool.
mapping(uint256 tokenId => mapping(address => uint256)) public votes;
/// @notice Mapping of epoch timestamps to the weights per pool for that epoch.
mapping(uint256 timestamp => mapping(address pool => uint256)) public weightsPerEpoch;
/// @notice Mapping of epoch timestamps to the total weights for that epoch.
mapping(uint256 timestamp => uint256) public totalWeightsPerEpoch;
/// @notice Indicates whether voting is currently paused.
/// @dev If set to true, voting functionality is paused, preventing votes from being cast or updated.
bool public votingPaused;
/// @notice Error to indicate that voting is currently paused.
/// @dev Reverts the transaction if any function protected by `whenNotVotingPaused` is called while voting is paused.
error DisableDuringVotingPaused();
/// @notice Modifier to check if voting is not paused.
/// @dev Reverts with `VotingPaused` if `votingPaused` is true, preventing the function execution.
modifier whenNotVotingPaused() {
if (votingPaused) {
revert DisableDuringVotingPaused();
}
_;
}
/**
* @notice Error thrown when the provided `percentageToLock` is invalid.
*/
error InvalidPercentageToLock();
/*//////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/
/**
* @notice Ensures that the caller is either the owner or approved for the specified NFT.
* @param tokenId_ The ID of the NFT to check.
*/
modifier onlyNftApprovedOrOwner(uint256 tokenId_) {
if (!IVotingEscrow(votingEscrow).isApprovedOrOwner(_msgSender(), tokenId_)) {
revert AccessDenied();
}
_;
}
/**
* @dev Constructor that initializes the BlastGovernorClaimableSetup with the given address and disables further initializers.
* @param blastGovernor_ The address of the BlastGovernor contract.
*/
constructor(address blastGovernor_) {
__BlastGovernorClaimableSetup_init(blastGovernor_);
_disableInitializers();
}
/**
* @notice Initializes the contract with the given parameters.
* @dev This function can only be called once during contract initialization.
* @param blastGovernor_ The address of the BlastGovernor contract.
* @param votingEscrow_ The address of the Voting Escrow contract.
*/
function initialize(address blastGovernor_, address votingEscrow_) external initializer {
__BlastGovernorClaimableSetup_init(blastGovernor_);
__ReentrancyGuard_init();
__AccessControl_init();
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
votingEscrow = votingEscrow_;
token = IVotingEscrow(votingEscrow_).token();
distributionWindowDuration = 3600;
}
/**
* @notice Updates the address of a specified contract.
* @dev Only callable by an address with the VOTER_ADMIN_ROLE.
* @param key_ The key representing the contract.
* @param value_ The new address of the contract.
* @custom:event UpdateAddress Emitted when a contract address is updated.
* @custom:error InvalidAddressKey Thrown when an invalid key is provided.
*/
function updateAddress(string memory key_, address value_) external onlyRole(_VOTER_ADMIN_ROLE) {
bytes32 key = keccak256(abi.encodePacked(key_));
if (key == 0x39eb9ec2059d897c44a17440c762c429de204f6fddd727156ca52b8da086a6f7) {
minter = value_;
} else if (key == 0xf23a19003b02ccc6ddd73a13c071e09977c34bfd7b5318a44fe456d9a77dd0af) {
bribeFactory = value_;
} else if (key == 0x18c95c463f9590b3f298aef56c7cfb639672452cd99ac8d92a9fc0e2ef46ab55) {
merklDistributor = value_;
} else if (key == 0xbbbfaae454470f56db24caaffaae3a4d3d0ed7a761421871150faa442416ea83) {
veFnxMerklAidrop = value_;
} else if (key == 0x8ba8cbf9a47db7b5e8ae6c0bff072ed6faefec4a0722891b09f22b7ac343fd4f) {
managedNFTManager = value_;
} else if (key == 0xa0238e972eab1b5ee9c4988c955a7165a662b3206031ac6ac27a3066d669a28d) {
v2PoolFactory = value_;
} else if (key == 0xb8e13a5900588d0607f820e1a839eb41b418c77b9db23e333bcc679d611dbc9b) {
v3PoolFactory = value_;
} else if (key == 0xe8ee2fdef59c2203ee9a363d82083446f25f27a1aff8fc1f0f3f79b83d30305c) {
v2GaugeFactory = value_;
} else if (key == 0x7ebf69e1e15f4a4db2cb161251ab5c47f9f68d65713eba9542fedffbe59b7931) {
v3GaugeFactory = value_;
} else {
revert InvalidAddressKey();
}
emit UpdateAddress(key_, value_);
}
/**
* @notice Sets the voting paused state.
* @dev Only callable by an address with the DEFAULT_ADMIN_ROLE.
* @param isPaused_ Indicates whether voting should be paused (true) or unpaused (false).
*/
function setVotingPause(bool isPaused_) external onlyRole(DEFAULT_ADMIN_ROLE) {
votingPaused = isPaused_;
emit VotingPaused(isPaused_);
}
/**
* @notice Sets the duration of the distribution window for voting.
* @dev Only callable by an address with the VOTER_ADMIN_ROLE.
* @param distributionWindowDuration_ The duration in seconds.
* @custom:event SetDistributionWindowDuration Emitted when the distribution window duration is updated.
*/
function setDistributionWindowDuration(uint256 distributionWindowDuration_) external onlyRole(_VOTER_ADMIN_ROLE) {
distributionWindowDuration = distributionWindowDuration_;
emit SetDistributionWindowDuration(distributionWindowDuration_);
}
/**
* @notice Disables a gauge, preventing further rewards distribution.
* @dev Only callable by an address with the GOVERNANCE_ROLE.
* @param gauge_ The address of the gauge to be disabled.
* @custom:event GaugeKilled Emitted when a gauge is disabled.
* @custom:error GaugeAlreadyKilled Thrown if the gauge is already disabled.
*/
function killGauge(address gauge_) external onlyRole(_GOVERNANCE_ROLE) {
GaugeState memory state = gaugesState[gauge_];
if (!state.isAlive) {
revert GaugeAlreadyKilled();
}
delete gaugesState[gauge_].isAlive;
if (state.claimable > 0) {
IERC20Upgradeable(token).safeTransfer(minter, state.claimable);
delete gaugesState[gauge_].claimable;
}
emit GaugeKilled(gauge_);
}
/**
* @notice Revives a previously disabled gauge, allowing it to distribute rewards again.
* @dev Only callable by an address with the GOVERNANCE_ROLE.
* @param gauge_ The address of the gauge to be revived.
* @custom:event GaugeRevived Emitted when a gauge is revived.
* @custom:error GaugeNotKilled Thrown if the gauge is not currently disabled.
*/
function reviveGauge(address gauge_) external onlyRole(_GOVERNANCE_ROLE) {
if (gaugesState[gauge_].isAlive) {
revert GaugeNotKilled();
}
gaugesState[gauge_].isAlive = true;
emit GaugeRevived(gauge_);
}
/**
* @notice Creates a new V2 gauge for a specified pool.
* @dev Only callable by an address with the GOVERNANCE_ROLE. The pool must be created by the V2 Pool Factory.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
* @custom:error GaugeForPoolAlreadyExists Thrown if a gauge already exists for the specified pool.
* @custom:error PoolNotCreatedByFactory Thrown if the specified pool was not created by the V2 Pool Factory.
*/
function createV2Gauge(
address pool_
) external nonReentrant onlyRole(_GOVERNANCE_ROLE) returns (address gauge, address internalBribe, address externalBribe) {
if (poolToGauge[pool_] != address(0x0)) {
revert GaugeForPoolAlreadyExists();
}
if (!IPairFactory(v2PoolFactory).isPair(pool_)) {
revert PoolNotCreatedByFactory();
}
address token0 = IPairIntegrationInfo(pool_).token0();
address token1 = IPairIntegrationInfo(pool_).token1();
address feeVault = IPairIntegrationInfo(pool_).communityVault();
if (feeVault == address(0)) {
revert PoolNotInitialized();
}
string memory symbol = IERC20MetadataUpgradeable(pool_).symbol();
IBribeFactory bribeFactoryCache = IBribeFactory(bribeFactory);
internalBribe = IBribeFactory(bribeFactoryCache).createBribe(token0, token1, string.concat("Fenix LP Fees: ", symbol));
externalBribe = IBribeFactory(bribeFactoryCache).createBribe(token0, token1, string.concat("Fenix Bribes: ", symbol));
gauge = IGaugeFactory(v2GaugeFactory).createGauge(
token,
votingEscrow,
pool_,
address(this),
internalBribe,
externalBribe,
false,
feeVault
);
_registerCreatedGauge(gauge, pool_, internalBribe, externalBribe);
v2Pools.push(pool_);
emit GaugeCreatedType(gauge, 0);
}
/**
* @notice Creates a new V3 gauge for a specified pool.
* @dev Only callable by an address with the GOVERNANCE_ROLE. The pool must be created by the V3 Pool Factory.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
* @custom:error GaugeForPoolAlreadyExists Thrown if a gauge already exists for the specified pool.
* @custom:error PoolNotCreatedByFactory Thrown if the specified pool was not created by the V3 Pool Factory.
*/
function createV3Gauge(
address pool_
) external nonReentrant onlyRole(_GOVERNANCE_ROLE) returns (address gauge, address internalBribe, address externalBribe) {
if (poolToGauge[pool_] != address(0x0)) {
revert GaugeForPoolAlreadyExists();
}
address token0 = IPairIntegrationInfo(pool_).token0();
address token1 = IPairIntegrationInfo(pool_).token1();
if (IAlgebraFactory(v3PoolFactory).poolByPair(token0, token1) != pool_) {
revert PoolNotCreatedByFactory();
}
address feeVault = IPairIntegrationInfo(pool_).communityVault();
if (feeVault == address(0)) {
revert PoolNotInitialized();
}
string memory symbol = string.concat(IERC20MetadataUpgradeable(token0).symbol(), "/", IERC20MetadataUpgradeable(token1).symbol());
IBribeFactory bribeFactoryCache = IBribeFactory(bribeFactory);
internalBribe = IBribeFactory(bribeFactoryCache).createBribe(token0, token1, string.concat("Fenix LP Fees: ", symbol));
externalBribe = IBribeFactory(bribeFactoryCache).createBribe(token0, token1, string.concat("Fenix Bribes: ", symbol));
gauge = IGaugeFactory(v3GaugeFactory).createGauge(
token,
votingEscrow,
pool_,
address(this),
internalBribe,
externalBribe,
true,
feeVault
);
_registerCreatedGauge(gauge, pool_, internalBribe, externalBribe);
v3Pools.push(pool_);
emit GaugeCreatedType(gauge, 1);
}
/**
* @notice Creates a custom gauge with specified parameters.
* @dev Only callable by an address with the GOVERNANCE_ROLE.
* @param gauge_ The address of the custom gauge.
* @param pool_ The address of the pool for which to create a gauge.
* @param tokenA_ The address of token A in the pool.
* @param tokenB_ The address of token B in the pool.
* @param externalBribesName_ The name of the external bribe.
* @param internalBribesName_ The name of the internal bribe.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
* @custom:error GaugeForPoolAlreadyExists Thrown if a gauge already exists for the specified pool.
*/
function createCustomGauge(
address gauge_,
address pool_,
address tokenA_,
address tokenB_,
string memory externalBribesName_,
string memory internalBribesName_
) external nonReentrant onlyRole(_GOVERNANCE_ROLE) returns (address gauge, address internalBribe, address externalBribe) {
if (poolToGauge[pool_] != address(0x0)) {
revert GaugeForPoolAlreadyExists();
}
gauge = gauge_;
IBribeFactory bribeFactoryCache = IBribeFactory(bribeFactory);
externalBribe = bribeFactoryCache.createBribe(tokenA_, tokenB_, externalBribesName_);
internalBribe = bribeFactoryCache.createBribe(tokenA_, tokenB_, internalBribesName_);
_registerCreatedGauge(gauge_, pool_, internalBribe, externalBribe);
emit GaugeCreatedType(gauge, 2);
}
/**
* @notice Notifies the contract of a reward amount to be distributed.
* @dev Only callable by the Minter contract.
* @param amount_ The amount of rewards to distribute.
* @custom:event NotifyReward Emitted when rewards are notified for distribution.
* @custom:error AccessDenied Thrown if the caller is not the Minter contract.
*/
function notifyRewardAmount(uint256 amount_) external {
if (_msgSender() != minter) {
revert AccessDenied();
}
IERC20Upgradeable(token).safeTransferFrom(_msgSender(), address(this), amount_);
uint256 weightAt = totalWeightsPerEpoch[epochTimestamp() - _WEEK]; // minter call notify after updates active_period, loads votes - 1 week
if (weightAt > 0) {
index += (amount_ * 1e18) / weightAt;
}
emit NotifyReward(_msgSender(), token, amount_);
}
/**
* @notice Distributes fees to a list of gauges.
* @dev Only gauges that are active and alive will receive fees.
* @param gauges_ An array of gauge addresses to distribute fees to.
*/
function distributeFees(address[] calldata gauges_) external {
for (uint256 i; i < gauges_.length; i++) {
GaugeState memory state = gaugesState[gauges_[i]];
if (state.isGauge && state.isAlive) {
IGauge(gauges_[i]).claimFees();
}
}
}
/**
* @notice Distributes rewards to all pools managed by the contract.
* @dev The Minter contract's update_period function is called before distributing rewards.
*/
function distributeAll() external nonReentrant {
IMinter(minter).update_period();
uint256 length = pools.length;
for (uint256 i; i < length; i++) {
_distribute(poolToGauge[pools[i]]);
}
}
/**
* @notice Distributes rewards to a specified range of pools.
* @dev The Minter contract's update_period function is called before distributing rewards.
* @param start_ The starting index of the pool array.
* @param finish_ The ending index of the pool array.
*/
function distribute(uint256 start_, uint256 finish_) external nonReentrant {
IMinter(minter).update_period();
for (uint256 i = start_; i < finish_; i++) {
_distribute(poolToGauge[pools[i]]);
}
}
/**
* @notice Distributes rewards to a specified list of gauges.
* @dev The Minter contract's update_period function is called before distributing rewards.
* @param gauges_ An array of gauge addresses to distribute rewards to.
*/
function distribute(address[] calldata gauges_) external nonReentrant {
IMinter(minter).update_period();
for (uint256 i; i < gauges_.length; i++) {
_distribute(gauges_[i]);
}
}
/**
* @notice Resets the votes for a given NFT token ID.
* @dev This function is non-reentrant and can only be called by the owner or an approved address for the token ID.
* @param tokenId_ The token ID for which to reset votes.
*/
function reset(uint256 tokenId_) external nonReentrant whenNotVotingPaused {
_revertIfNotVotingEscrowOrApprovedOrOwner(tokenId_);
_checkVoteDelay(tokenId_);
_checkVoteWindow();
_reset(tokenId_);
_updateLastVotedTimestamp(tokenId_);
IVotingEscrow(votingEscrow).votingHook(tokenId_, false);
}
/**
* @notice Updates the voting preferences for a given token ID.
* @dev This function is non-reentrant and can only be called by the owner or an approved address for the token ID.
* @param tokenId_ The token ID for which to update voting preferences.
*/
function poke(uint256 tokenId_) external nonReentrant whenNotVotingPaused onlyNftApprovedOrOwner(tokenId_) {
_checkStartVoteWindow();
IManagedNFTManager managedNFTManagerCache = IManagedNFTManager(managedNFTManager);
if (managedNFTManagerCache.isDisabledNFT(tokenId_)) {
revert DisabledManagedNft();
}
if (!managedNFTManagerCache.isWhitelistedNFT(tokenId_)) {
_checkEndVoteWindow();
}
_poke(tokenId_);
_updateLastVotedTimestamp(tokenId_);
}
/**
* @notice Casts votes for a given NFT token ID.
* @dev The function ensures that the vote delay has passed and checks the vote window before allowing the vote.
* @param tokenId_ The token ID for which to cast votes.
* @param poolsVotes_ An array of pool addresses to vote for.
* @param weights_ An array of weights corresponding to the pools.
* @custom:error ArrayLengthMismatch Thrown if the length of poolsVotes_ and weights_ arrays do not match.
* @custom:error DisabledManagedNft Thrown if the NFT is disabled.
* @custom:error ZeroPowerForPool Thrown if the calculated vote power for a pool is zero.
* @custom:error GaugeAlreadyKilled Thrown if attempting to vote for a killed gauge.
* @custom:error NoResetBefore Thrown if there was no prior reset before voting.
*/
function vote(
uint256 tokenId_,
address[] calldata poolsVotes_,
uint256[] calldata weights_
) external nonReentrant whenNotVotingPaused onlyNftApprovedOrOwner(tokenId_) {
if (poolsVotes_.length != weights_.length) {
revert ArrayLengthMismatch();
}
_checkStartVoteWindow();
IManagedNFTManager managedNFTManagerCache = IManagedNFTManager(managedNFTManager);
if (managedNFTManagerCache.isDisabledNFT(tokenId_)) {
revert DisabledManagedNft();
}
if (!managedNFTManagerCache.isWhitelistedNFT(tokenId_)) {
_checkEndVoteWindow();
_checkVoteDelay(tokenId_);
}
_vote(tokenId_, poolsVotes_, weights_);
_updateLastVotedTimestamp(tokenId_);
}
/**
* @notice Claims rewards from multiple gauges.
* @param _gauges An array of gauge addresses to claim rewards from.
*/
function claimRewards(address[] memory _gauges) public {
for (uint256 i; i < _gauges.length; i++) {
IGauge(_gauges[i]).getReward(_msgSender());
}
}
/**
* @notice Claims bribes for a given NFT token ID from multiple bribe contracts.
* @dev This function can only be called by the owner or an approved address for the token ID.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
* @param tokenId_ The token ID for which to claim bribes.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 tokenId_) public onlyNftApprovedOrOwner(tokenId_) {
for (uint256 i; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForOwner(tokenId_, _tokens[i]);
}
}
/**
* @notice Claims bribes from multiple bribe contracts.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens) public {
for (uint256 i; i < _bribes.length; i++) {
IBribe(_bribes[i]).getRewardForAddress(_msgSender(), _tokens[i]);
}
}
/**
* @notice Attaches a tokenId to a managed tokenId.
* @dev Requires the sender to be the owner or approved on the voting escrow contract.
* @param tokenId_ The user's tokenId to be attached.
* @param managedTokenId_ The managed tokenId to attach to.
* @custom:event AttachToManagedNFT Emitted when a tokenId is attached to a managed tokenId.
*/
function attachToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external whenNotVotingPaused {
_revertIfNotVotingEscrowOrApprovedOrOwner(tokenId_);
_checkVoteDelay(tokenId_);
_checkVoteWindow();
IManagedNFTManager(managedNFTManager).onAttachToManagedNFT(tokenId_, managedTokenId_);
_poke(managedTokenId_);
_updateLastVotedTimestamp(tokenId_);
_updateLastVotedTimestamp(managedTokenId_);
emit AttachToManagedNFT(tokenId_, managedTokenId_);
}
/**
* @notice Detaches a tokenId from its managed tokenId.
* @dev Requires the sender to be the owner or approved. Also adjusts the voting weight post-detachment.
* @param tokenId_ The user's tokenId to be detached.
* @custom:event DettachFromManagedNFT Emitted when a tokenId is detached from a managed tokenId.
*/
function dettachFromManagedNFT(uint256 tokenId_) external whenNotVotingPaused {
_revertIfNotVotingEscrowOrApprovedOrOwner(tokenId_);
_checkVoteDelay(tokenId_);
_checkVoteWindow();
IManagedNFTManager managedNFTManagerCache = IManagedNFTManager(managedNFTManager);
uint256 managedTokenId = managedNFTManagerCache.getAttachedManagedTokenId(tokenId_);
managedNFTManagerCache.onDettachFromManagedNFT(tokenId_);
uint256 weight = IVotingEscrow(votingEscrow).balanceOfNftIgnoreOwnershipChange(managedTokenId);
if (weight == 0) {
_reset(managedTokenId);
} else {
_poke(managedTokenId);
}
_updateLastVotedTimestamp(tokenId_);
_updateLastVotedTimestamp(managedTokenId);
emit DettachFromManagedNFT(tokenId_);
}
/**
* @notice Handles the deposit of voting power to a managed NFT.
* @dev This function is called after tokens are deposited into the Voting Escrow contract for a managed NFT.
* Only callable by the Voting Escrow contract.
* @param managedTokenId_ The ID of the managed token receiving the voting power.
* @custom:error AccessDenied Thrown if the caller is not the Voting Escrow contract.
*/
function onDepositToManagedNFT(uint256 /**tokenId_**/, uint256 managedTokenId_) external nonReentrant whenNotVotingPaused {
address votingEscrowCache = votingEscrow;
if (_msgSender() != votingEscrowCache) {
revert AccessDenied();
}
_checkVoteWindow();
_poke(managedTokenId_);
_updateLastVotedTimestamp(managedTokenId_);
}
/**
* @notice Aggregates multiple claim calls into a single transaction and optionally locks claimed tokens.
* @dev This method allows users to claim rewards, bribes, and airdrops from multiple sources,
* and optionally locks a percentage of the claimed reward tokens into a veNFT.
* @param gauges_ The array of gauge addresses to claim rewards from.
* @param bribes_ The parameters for claiming bribes without specifying a token ID.
* @param bribesByTokenId_ The parameters for claiming bribes associated with a specific token ID.
* @param merkl_ The parameters for claiming rewards using Merkl distributor.
* @param splitMerklAidrop_ The parameters for claiming VeFnx Merkl airdrop data.
* @param aggregateCreateLock_ The parameters for locking a percentage of the claimed rewards into a veNFT.
*
* Functionality:
* - Claims rewards from gauges.
* - Claims bribes, both with and without token IDs.
* - Claims Merkl-based airdrops.
* - Claims VeFnx-based Merkl airdrops.
* - Converts a specified percentage of claimed reward tokens into a veNFT lock.
*/
function aggregateClaim(
address[] calldata gauges_,
AggregateClaimBribesParams calldata bribes_,
AggregateClaimBribesByTokenIdParams calldata bribesByTokenId_,
AggregateClaimMerklDataParams calldata merkl_,
AggregateClaimVeFnxMerklAirdrop calldata splitMerklAidrop_,
AggregateCreateLockParams calldata aggregateCreateLock_
) external {
IERC20Upgradeable tokenCache = IERC20Upgradeable(token);
uint256 userBalanceBefore = aggregateCreateLock_.percentageToLock > 0 ? tokenCache.balanceOf(_msgSender()) : 0;
{
if (gauges_.length > 0) {
claimRewards(gauges_);
}
if (bribes_.bribes.length > 0) {
claimBribes(bribes_.bribes, bribes_.tokens);
}
if (bribesByTokenId_.bribes.length > 0) {
claimBribes(bribesByTokenId_.bribes, bribesByTokenId_.tokens, bribesByTokenId_.tokenId);
}
if (merkl_.users.length > 0) {
for (uint256 i; i < merkl_.users.length; ) {
require(merkl_.users[i] == _msgSender(), "users containes no only caller");
unchecked {
i++;
}
}
IMerklDistributor(merklDistributor).claim(merkl_.users, merkl_.tokens, merkl_.amounts, merkl_.proofs);
}
if (splitMerklAidrop_.amount > 0) {
IVeFnxSplitMerklAidrop(veFnxMerklAidrop).claimFor(
_msgSender(),
splitMerklAidrop_.inPureTokens,
splitMerklAidrop_.amount,
splitMerklAidrop_.withPermanentLock,
splitMerklAidrop_.managedTokenIdForAttach,
splitMerklAidrop_.proofs
);
}
}
if (aggregateCreateLock_.percentageToLock > 0) {
if (aggregateCreateLock_.percentageToLock > 1e18) {
revert InvalidPercentageToLock();
}
uint256 amount = ((tokenCache.balanceOf(_msgSender()) - userBalanceBefore) * aggregateCreateLock_.percentageToLock) / 1e18;
if (amount > 0) {
IVotingEscrow votingEscrowCache = IVotingEscrow(votingEscrow);
tokenCache.safeTransferFrom(_msgSender(), address(this), amount);
tokenCache.safeApprove(address(votingEscrowCache), amount);
votingEscrowCache.createLockFor(
amount,
aggregateCreateLock_.lockDuration,
aggregateCreateLock_.to,
aggregateCreateLock_.shouldBoosted,
aggregateCreateLock_.withPermanentLock,
aggregateCreateLock_.managedTokenIdForAttach
);
}
}
}
/**
* @notice Returns the total number of pools, V2 pools, and V3 pools managed by the contract.
* @return totalCount The total number of pools.
* @return v2PoolsCount The total number of V2 pools.
* @return v3PoolsCount The total number of V3 pools.
*/
function poolsCounts() external view returns (uint256 totalCount, uint256 v2PoolsCount, uint256 v3PoolsCount) {
return (pools.length, v2Pools.length, v3Pools.length);
}
/**
* @notice Checks if the provided address is a registered gauge.
* @param gauge_ The address of the gauge to check.
* @return True if the address is a registered gauge, false otherwise.
*/
function isGauge(address gauge_) external view returns (bool) {
return gaugesState[gauge_].isGauge;
}
/**
* @notice Returns the number of pools that an NFT token ID has voted for.
* @param tokenId The ID of the NFT.
* @return The number of pools the token has voted for.
*/
function poolVoteLength(uint256 tokenId) external view returns (uint256) {
return poolVote[tokenId].length;
}
/**
* @notice Checks if the specified gauge is alive (i.e., enabled for reward distribution).
* @param gauge_ The address of the gauge to check.
* @return True if the gauge is alive, false otherwise.
*/
function isAlive(address gauge_) external view returns (bool) {
return gaugesState[gauge_].isAlive;
}
/**
* @notice Returns the state of a specific gauge.
* @param gauge_ The address of the gauge.
* @return GaugeState The current state of the specified gauge.
*/
function getGaugeState(address gauge_) external view returns (GaugeState memory) {
return gaugesState[gauge_];
}
/**
* @notice Returns the pool address associated with a specified gauge.
* @param gauge_ The address of the gauge to query.
* @return The address of the pool associated with the specified gauge.
*/
function poolForGauge(address gauge_) external view returns (address) {
return gaugesState[gauge_].pool;
}
/**
* @dev Updates the voting preferences for a given tokenId after changes in the system.
* @param tokenId_ The tokenId for which to update voting preferences.
*/
function _poke(uint256 tokenId_) internal {
address[] memory _poolVote = poolVote[tokenId_];
uint256[] memory _weights = new uint256[](_poolVote.length);
for (uint256 i; i < _poolVote.length; ) {
_weights[i] = votes[tokenId_][_poolVote[i]];
unchecked {
i++;
}
}
_vote(tokenId_, _poolVote, _weights);
}
/// @notice distribute the emission
function _distribute(address gauge_) internal {
GaugeState memory state = gaugesState[gauge_];
uint256 currentTimestamp = epochTimestamp();
if (state.lastDistributionTimestamp < currentTimestamp) {
uint256 totalVotesWeight = weightsPerEpoch[currentTimestamp - _WEEK][state.pool];
if (totalVotesWeight > 0) {
uint256 delta = index - state.index;
if (delta > 0) {
uint256 amount = (totalVotesWeight * delta) / 1e18;
if (state.isAlive) {
gaugesState[gauge_].claimable += amount;
} else {
IERC20Upgradeable(token).safeTransfer(minter, amount);
}
}
}
gaugesState[gauge_].index = index;
uint256 claimable = gaugesState[gauge_].claimable;
if (claimable > 0 && state.isAlive) {
gaugesState[gauge_].claimable = 0;
gaugesState[gauge_].lastDistributionTimestamp = currentTimestamp;
IERC20Upgradeable(token).approve(gauge_, claimable);
IGauge(gauge_).notifyRewardAmount(token, claimable);
emit DistributeReward(_msgSender(), gauge_, claimable);
}
}
}
/**
* @dev Registers a newly created gauge and associates it with a pool and bribes.
* @param gauge_ The address of the created gauge.
* @param pool_ The address of the pool associated with the gauge.
* @param internalBribe_ The address of the associated internal bribe.
* @param externalBribe_ The address of the associated external bribe.
*/
function _registerCreatedGauge(address gauge_, address pool_, address internalBribe_, address externalBribe_) internal {
gaugesState[gauge_] = GaugeState({
isGauge: true,
isAlive: true,
internalBribe: internalBribe_,
externalBribe: externalBribe_,
pool: pool_,
claimable: 0,
index: index,
lastDistributionTimestamp: 0
});
poolToGauge[pool_] = gauge_;
pools.push(pool_);
emit GaugeCreated(gauge_, _msgSender(), internalBribe_, externalBribe_, pool_);
}
/**
* @notice Returns the current epoch timestamp used for reward calculations.
* @return The current epoch timestamp.
*/
function epochTimestamp() public view returns (uint256) {
return IMinter(minter).active_period();
}
/**
* @dev Resets the votes for a given token ID.
* @param tokenId_ The token ID for which to reset votes.
*/
function _reset(uint256 tokenId_) internal {
address[] memory votesPools = poolVote[tokenId_];
uint256 totalVotePowerForPools;
uint256 time = epochTimestamp();
uint256 lastVotedTime = lastVotedTimestamps[tokenId_];
for (uint256 i; i < votesPools.length; i++) {
address pool = votesPools[i];
uint256 votePowerForPool = votes[tokenId_][pool];
if (votePowerForPool > 0) {
delete votes[tokenId_][pool];
if (lastVotedTime >= time) {
weightsPerEpoch[time][pool] -= votePowerForPool;
address gauge = poolToGauge[pool];
IBribe(gaugesState[gauge].internalBribe).withdraw(votePowerForPool, tokenId_);
IBribe(gaugesState[gauge].externalBribe).withdraw(votePowerForPool, tokenId_);
if (gaugesState[gauge].isAlive) {
totalVotePowerForPools += votePowerForPool;
}
}
}
}
if (lastVotedTime >= time) {
totalWeightsPerEpoch[time] -= totalVotePowerForPools;
}
delete poolVote[tokenId_];
emit VoteReset(_msgSender(), tokenId_, time, totalVotePowerForPools);
}
/**
* @dev Casts votes for a given NFT token ID.
* @param tokenId_ The token ID for which to cast votes.
* @param pools_ An array of pool addresses to vote for.
* @param weights_ An array of weights corresponding to the pools.
*/
function _vote(uint256 tokenId_, address[] memory pools_, uint256[] memory weights_) internal {
_reset(tokenId_);
uint256 nftVotePower = IVotingEscrow(votingEscrow).balanceOfNFT(tokenId_);
uint256 totalVotesWeight;
uint256 totalVoterPower;
for (uint256 i; i < pools_.length; i++) {
GaugeState memory state = gaugesState[poolToGauge[pools_[i]]];
if (!state.isAlive) {
revert GaugeAlreadyKilled();
}
totalVotesWeight += weights_[i];
}
uint256 time = epochTimestamp();
uint256[] memory voteWeights = new uint256[](pools_.length);
for (uint256 i; i < pools_.length; i++) {
address pool = pools_[i];
address gauge = poolToGauge[pools_[i]];
uint256 votePowerForPool = (weights_[i] * nftVotePower) / totalVotesWeight;
if (votePowerForPool == 0) {
revert ZeroPowerForPool();
}
if (votes[tokenId_][pool] > 0) {
revert NoResetBefore();
}
poolVote[tokenId_].push(pool);
votes[tokenId_][pool] = votePowerForPool;
weightsPerEpoch[time][pool] += votePowerForPool;
totalVoterPower += votePowerForPool;
voteWeights[i] = votePowerForPool;
IBribe(gaugesState[gauge].internalBribe).deposit(votePowerForPool, tokenId_);
IBribe(gaugesState[gauge].externalBribe).deposit(votePowerForPool, tokenId_);
}
if (totalVoterPower > 0) IVotingEscrow(votingEscrow).votingHook(tokenId_, true);
totalWeightsPerEpoch[time] += totalVoterPower;
if (totalVoterPower > 0) {
emit VoteCast(_msgSender(), tokenId_, time, pools_, voteWeights, totalVoterPower);
}
}
/**
* @dev Updates the last voted timestamp for a given token ID.
* @param tokenId_ The token ID for which to update the last voted timestamp.
*/
function _updateLastVotedTimestamp(uint256 tokenId_) internal {
lastVotedTimestamps[tokenId_] = epochTimestamp() + 1;
}
/**
* @dev Ensures that the current time is within the allowed voting window.
*/
function _checkVoteWindow() internal view {
_checkStartVoteWindow();
_checkEndVoteWindow();
}
/**
* @dev Ensures that the current time is after the start of the voting window.
* @custom:error DistributionWindow Thrown if the current time is before the start of the voting window.
*/
function _checkStartVoteWindow() internal view {
if (block.timestamp <= (block.timestamp - (block.timestamp % _WEEK) + distributionWindowDuration)) {
revert DistributionWindow();
}
}
/**
* @dev Ensures that the current time is before the end of the voting window.
* @custom:error DistributionWindow Thrown if the current time is after the end of the voting window.
*/
function _checkEndVoteWindow() internal view {
if (block.timestamp >= (block.timestamp - (block.timestamp % _WEEK) + _WEEK - distributionWindowDuration)) {
revert DistributionWindow();
}
}
/**
* @dev Ensures that the required delay period has passed since the last vote.
* @param tokenId_ The token ID to check.
* @custom:error VoteDelay Thrown if the required delay period has not passed.
*/
function _checkVoteDelay(uint256 tokenId_) internal view {
if (block.timestamp < lastVotedTimestamps[tokenId_] + voteDelay) {
revert VoteDelay();
}
}
/**
* @notice Ensures the caller is either the VotingEscrow contract itself or is approved/owner of the specified veNFT.
* @dev If the caller is neither the `votingEscrow` contract nor an approved/owner of `tokenId_`, the transaction reverts.
* @param tokenId_ The ID of the veNFT to check.
* @custom:reverts AccessDenied if the caller is not allowed to operate on the token.
*/
function _revertIfNotVotingEscrowOrApprovedOrOwner(uint256 tokenId_) internal view {
IVotingEscrow votingEscrowCache = IVotingEscrow(votingEscrow);
if (_msgSender() != address(votingEscrowCache)) {
if (!votingEscrowCache.isApprovedOrOwner(_msgSender(), tokenId_)) {
revert AccessDenied();
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
pragma abicoder v2;
import './plugin/IAlgebraPluginFactory.sol';
import './vault/IAlgebraVaultFactory.sol';
import './IERC20Rebasing.sol';
/// @title The interface for the Algebra Factory
/// @dev Credit to Uniswap Labs under GPL-2.0-or-later license:
/// https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces
interface IAlgebraFactory {
/// @notice Emitted when a process of ownership renounce is started
/// @param timestamp The timestamp of event
/// @param finishTimestamp The timestamp when ownership renounce will be possible to finish
event RenounceOwnershipStart(uint256 timestamp, uint256 finishTimestamp);
/// @notice Emitted when a process of ownership renounce cancelled
/// @param timestamp The timestamp of event
event RenounceOwnershipStop(uint256 timestamp);
/// @notice Emitted when a process of ownership renounce finished
/// @param timestamp The timestamp of ownership renouncement
event RenounceOwnershipFinish(uint256 timestamp);
/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param pool The address of the created pool
event Pool(address indexed token0, address indexed token1, address pool);
/// @notice Emitted when the default community fee is changed
/// @param newDefaultCommunityFee The new default community fee value
event DefaultCommunityFee(uint16 newDefaultCommunityFee);
/// @notice Emitted when the default tickspacing is changed
/// @param newDefaultTickspacing The new default tickspacing value
event DefaultTickspacing(int24 newDefaultTickspacing);
/// @notice Emitted when the default fee is changed
/// @param newDefaultFee The new default fee value
event DefaultFee(uint16 newDefaultFee);
/// @notice Emitted when the defaultPluginFactory address is changed
/// @param defaultPluginFactoryAddress The new defaultPluginFactory address
event DefaultPluginFactory(address defaultPluginFactoryAddress);
/// @notice Emitted when the vaultFactory address is changed
/// @param newVaultFactory The new vaultFactory address
event VaultFactory(address newVaultFactory);
/// @notice Emitted when the pools creation mode is changed
/// @param mode_ The new pools creation mode
event PublicPoolCreationMode(bool mode_);
/// @dev Emitted when set new default blast governor address is changed.
/// @param defaultBlastGovernor The new default blast governor address
event DefaultBlastGovernor(address indexed defaultBlastGovernor);
/// @dev Emitted when set new default blast points address is changed.
/// @param defaultBlastPoints The new default blast points address
event DefaultBlastPoints(address indexed defaultBlastPoints);
/// @dev Emitted when set new default blast points operator address is changed.
/// @param defaultBlastPointsOperator The new default blast points operator address
event DefaultBlastPointsOperator(address indexed defaultBlastPointsOperator);
/// @notice Emitted when the rebase configuration for a token is set or updated
/// @param token The address of the token whose rebase configuration has been set or updated
/// @param isRebase Indicates whether the token is set as a rebasing token
/// @param mode The yield mode that has been set for the token, defining its rebasing behavior
event ConfigurationForRebaseToken(address token, bool isRebase, YieldMode mode);
/// @dev Emitted when the rebasing tokens governor address is set.
/// @param oldRebasingTokensGovernor The previous address of the rebasing tokens governor.
/// @param newRebasingTokensGovernor The new address of the rebasing tokens governor.
event SetRebasingTokensGovernor(address indexed oldRebasingTokensGovernor, address indexed newRebasingTokensGovernor);
/// @notice role that can change communityFee and tickspacing in pools
/// @return The hash corresponding to this role
function POOLS_ADMINISTRATOR_ROLE() external view returns (bytes32);
/// @notice role that can create pools when public pool creation is disabled
/// @return The hash corresponding to this role
function POOLS_CREATOR_ROLE() external view returns (bytes32);
/// @notice Returns `true` if `account` has been granted `role` or `account` is owner.
/// @param role The hash corresponding to the role
/// @param account The address for which the role is checked
/// @return bool Whether the address has this role or the owner role or not
function hasRoleOrOwner(bytes32 role, address account) external view returns (bool);
/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via transferOwnership(address newOwner)
/// @return The address of the factory owner
function owner() external view returns (address);
/// @notice Returns the current default blast governor
/// @return The address of the default blast governor
function defaultBlastGovernor() external view returns (address);
/// @notice Returns the current default blast points
/// @return The address of the default blast points
function defaultBlastPoints() external view returns (address);
/// @notice Returns the current default blast points operator
/// @return The address of the default blast points operator
function defaultBlastPointsOperator() external view returns (address);
/// @notice Retrieves the yield mode configuration for a specified token
/// @param token The address of the token for which to retrieve the yield mode
/// @return The yield mode (rebasing configuration) set for the given token
function configurationForBlastRebaseTokens(address token) external view returns (YieldMode);
/// @notice Return if a token is marked as a rebasing token in the factory configuration
/// @param token The address of the token to check
/// @return True if the token is a rebasing token, false otherwise
function isRebaseToken(address token) external view returns (bool);
/// @notice Returns the current poolDeployerAddress
/// @return The address of the poolDeployer
function poolDeployer() external view returns (address);
/// @notice Returns the status of enable public pool creation mode
/// @return bool Whether the public creation mode is enable or not
function isPublicPoolCreationMode() external view returns (bool);
/// @notice Returns the default community fee
/// @return Fee which will be set at the creation of the pool
function defaultCommunityFee() external view returns (uint16);
/// @notice Returns the default fee
/// @return Fee which will be set at the creation of the pool
function defaultFee() external view returns (uint16);
/// @notice Returns the default tickspacing
/// @return Tickspacing which will be set at the creation of the pool
function defaultTickspacing() external view returns (int24);
/// @notice Return the current pluginFactory address
/// @dev This contract is used to automatically set a plugin address in new liquidity pools
/// @return Algebra plugin factory
function defaultPluginFactory() external view returns (IAlgebraPluginFactory);
/// @notice Address of the rebasing tokens governor
/// @return rebasing tokens governor
function rebasingTokensGovernor() external view returns (address);
/// @notice Return the current vaultFactory address
/// @dev This contract is used to automatically set a vault address in new liquidity pools
/// @return Algebra vault factory
function vaultFactory() external view returns (IAlgebraVaultFactory);
/// @notice Returns the default communityFee, tickspacing, fee and communityFeeVault for pool
/// @param pool the address of liquidity pool
/// @return communityFee which will be set at the creation of the pool
/// @return tickSpacing which will be set at the creation of the pool
/// @return fee which will be set at the creation of the pool
/// @return communityFeeVault the address of communityFeeVault
function defaultConfigurationForPool(
address pool
) external view returns (uint16 communityFee, int24 tickSpacing, uint16 fee, address communityFeeVault);
/// @notice Deterministically computes the pool address given the token0 and token1
/// @dev The method does not check if such a pool has been created
/// @param token0 first token
/// @param token1 second token
/// @return pool The contract address of the Algebra pool
function computePoolAddress(address token0, address token1) external view returns (address pool);
/// @notice Returns the pool address for a given pair of tokens, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @return pool The pool address
function poolByPair(address tokenA, address tokenB) external view returns (address pool);
/// @notice returns keccak256 of AlgebraPool init bytecode.
/// @dev the hash value changes with any change in the pool bytecode
/// @return Keccak256 hash of AlgebraPool contract init bytecode
function POOL_INIT_CODE_HASH() external view returns (bytes32);
/// @return timestamp The timestamp of the beginning of the renounceOwnership process
function renounceOwnershipStartTimestamp() external view returns (uint256 timestamp);
/// @notice Creates a pool for the given two tokens
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0.
/// The call will revert if the pool already exists or the token arguments are invalid.
/// @return pool The address of the newly created pool
function createPool(address tokenA, address tokenB) external returns (address pool);
/// @dev updates pools creation mode
/// @param mode_ the new mode for pools creation proccess
function setIsPublicPoolCreationMode(bool mode_) external;
/// @notice Sets the rebase configuration for a specific token
/// @param token_ The address of the token to configure
/// @param isRebase_ A boolean indicating whether the token is a rebasing token or not
/// @param mode_ The yield mode to apply, defining how the rebasing mechanism should operate
function setConfigurationForRebaseToken(address token_, bool isRebase_, YieldMode mode_) external;
/// @notice Sets the address of the rebasing tokens governor.
/// @dev Updates the address of the rebasing tokens governor. Can only be called by an account with the DEFAULT_ADMIN_ROLE.
/// @param rebasingTokensGovernor_ The new address of the rebasing tokens governor.
/// Emits a {SetRebasingTokensGovernor} event.
function setRebasingTokensGovernor(address rebasingTokensGovernor_) external;
/// @dev updates default community fee for new pools
/// @param newDefaultCommunityFee The new community fee, _must_ be <= MAX_COMMUNITY_FEE
function setDefaultCommunityFee(uint16 newDefaultCommunityFee) external;
/// @dev updates default fee for new pools
/// @param newDefaultFee The new fee, _must_ be <= MAX_DEFAULT_FEE
function setDefaultFee(uint16 newDefaultFee) external;
/// @dev updates default tickspacing for new pools
/// @param newDefaultTickspacing The new tickspacing, _must_ be <= MAX_TICK_SPACING and >= MIN_TICK_SPACING
function setDefaultTickspacing(int24 newDefaultTickspacing) external;
/// @dev updates pluginFactory address
/// @param newDefaultPluginFactory address of new plugin factory
function setDefaultPluginFactory(address newDefaultPluginFactory) external;
/// @dev updates vaultFactory address
/// @param newVaultFactory address of new vault factory
function setVaultFactory(address newVaultFactory) external;
/// @notice Starts process of renounceOwnership. After that, a certain period
/// of time must pass before the ownership renounce can be completed.
function startRenounceOwnership() external;
/// @notice Stops process of renounceOwnership and removes timer.
function stopRenounceOwnership() external;
/// @dev updates default blast governor address on the factory
/// @param defaultBlastGovernor_ The new defautl blast governor address
function setDefaultBlastGovernor(address defaultBlastGovernor_) external;
/// @dev updates default blast points address on the factory
/// @param defaultBlastPoints_ The new defautl blast points address
function setDefaultBlastPoints(address defaultBlastPoints_) external;
/// @dev updates default blast points operator address on the factory
/// @param defaultBlastPointsOperator_ The new defautl blast points operator address
function setDefaultBlastPointsOperator(address defaultBlastPointsOperator_) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
interface IERC20Rebasing {
// changes the yield mode of the caller and update the balance
// to reflect the configuration
function configure(YieldMode) external returns (uint256);
// "claimable" yield mode accounts can call this this claim their yield
// to another address
function claim(address recipient, uint256 amount) external returns (uint256);
// read the claimable amount for an account
function getClaimableAmount(address account) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title An interface for a contract that is capable of deploying Algebra plugins
/// @dev Such a factory is needed if the plugin should be automatically created and connected to each new pool
interface IAlgebraPluginFactory {
/// @notice Deploys new plugin contract for pool
/// @param pool The address of the pool for which the new plugin will be created
/// @param token0 First token of the pool
/// @param token1 Second token of the pool
/// @return New plugin address
function createPlugin(address pool, address token0, address token1) external returns (address);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title The interface for the Algebra Vault Factory
/// @notice This contract can be used for automatic vaults creation
/// @dev Version: Algebra Integral
interface IAlgebraVaultFactory {
/// @notice returns address of the community fee vault for the pool
/// @param pool the address of Algebra Integral pool
/// @return communityFeeVault the address of community fee vault
function getVaultForPool(address pool) external view returns (address communityFeeVault);
/// @notice creates the community fee vault for the pool if needed
/// @param pool the address of Algebra Integral pool
/// @return communityFeeVault the address of community fee vault
function createVaultForPool(address pool) external returns (address communityFeeVault);
/// @notice Hook for calling after pool deployment
/// @param pool the address of Algebra Integral pool
function afterPoolInitialize(address pool) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20MetadataUpgradeable is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== 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 IERC20PermitUpgradeable {
/**
* @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 v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20PermitUpgradeable token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20Upgradeable 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))) && AddressUpgradeable.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721Upgradeable is IERC165Upgradeable {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = MathUpgradeable.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, MathUpgradeable.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IBribe {
struct Reward {
uint256 periodFinish;
uint256 rewardsPerEpoch;
uint256 lastUpdateTime;
}
/* ========== EVENTS ========== */
event RewardAdded(address indexed rewardToken, uint256 reward, uint256 startTimestamp);
event Staked(uint256 indexed tokenId, uint256 amount);
event Withdrawn(uint256 indexed tokenId, uint256 amount);
event RewardPaid(address indexed user, address indexed rewardsToken, uint256 reward);
event Recovered(address indexed token, uint256 amount);
event AddRewardToken(address indexed token);
function deposit(uint amount, uint tokenId) external;
function withdraw(uint amount, uint tokenId) external;
function getRewardTokens() external view returns (address[] memory);
function getSpecificRewardTokens() external view returns (address[] memory);
function getRewardForOwner(uint tokenId, address[] memory tokens) external;
function getRewardForAddress(address _owner, address[] memory tokens) external;
function notifyRewardAmount(address token, uint amount) external;
function addRewardToken(address) external;
function addRewardTokens(address[] memory) external;
function initialize(address, address, address, string memory) external;
function firstBribeTimestamp() external view returns (uint256);
function totalSupplyAt(uint256 timestamp) external view returns (uint256);
function rewardData(address, uint256) external view returns (uint256 periodFinish, uint256 rewardsPerEpoch, uint256 lastUpdateTime);
function rewardsListLength() external view returns (uint256);
function getEpochStart() external view returns (uint256);
function earned(uint256 tokenId, address _rewardToken) external view returns (uint256);
function earned(address _owner, address _rewardToken) external view returns (uint256);
function balanceOfAt(uint256 tokenId, uint256 _timestamp) external view returns (uint256);
function balanceOf(uint256 tokenId) external view returns (uint256);
function getNextEpochStart() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IBribeFactory {
/**
* @dev Emitted when the voter address is updated. This address is used for voting in fee vaults.
*
* @param oldVoter The address of the previous voter.
* @param newVoter The address of the new voter that has been set.
*/
event SetVoter(address indexed oldVoter, address indexed newVoter);
/**
* @dev Emitted when the default Blast governor address for new bribes is updated.
*
* @param oldDefaultBlastGovernor The address of the previous default Blast governor.
* @param newDefaultBlastGovernor The address of the new default Blast governor that has been set.
*/
event SetDefaultBlastGovernor(address indexed oldDefaultBlastGovernor, address indexed newDefaultBlastGovernor);
event bribeImplementationChanged(address _oldbribeImplementation, address _newbribeImplementation);
event AddDefaultRewardToken(address indexed token);
event RemoveDefaultRewardToken(address indexed token);
event PauseRewardClaim(bool indexed isPaused_);
function createBribe(address _token0, address _token1, string memory _type) external returns (address);
function bribeImplementation() external view returns (address impl);
function bribeOwner() external view returns (address owner);
function isDefaultRewardToken(address token_) external view returns (bool);
function getDefaultRewardTokens() external view returns (address[] memory);
function getBribeRewardTokens(address bribe_) external view returns (address[] memory);
function isRewardClaimPause() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMinter {
event Mint(address indexed sender, uint256 weekly, uint256 circulating_supply);
function update_period() external returns (uint);
function check() external view returns (bool);
function period() external view returns (uint);
function active_period() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
/**
* @title IVeFnxSplitMerklAidrop
* @dev Interface for the VeFnxSplitMerklAidropUpgradeable contract.
*/
interface IVeFnxSplitMerklAidrop {
/**
* @dev Emitted when a user claims their tokens.
* @param user The address of the user.
* @param claimAmount The total amount of tokens claimed.
* @param toTokenAmount The amount of tokens transferred directly to the user.
* @param toVeNFTAmount The amount of tokens locked as veNFT.
* @param tokenId The ID of the veNFT lock created.
*/
event Claim(address indexed user, uint256 claimAmount, uint256 toTokenAmount, uint256 toVeNFTAmount, uint256 tokenId);
/**
* @dev Emitted when the Merkle root is set.
* @param merklRoot The new Merkle root.
*/
event SetMerklRoot(bytes32 merklRoot);
/**
* @dev Emitted when the pure tokens rate is set.
* @param pureTokensRate The new pure tokens rate.
*/
event SetPureTokensRate(uint256 pureTokensRate);
/**
* @dev Emitted when the allowed status of a claim operator is set or changed.
* @param operator The address of the claim operator.
* @param isAllowed A boolean indicating whether the operator is allowed.
*/
event SetIsAllowedClaimOperator(address indexed operator, bool indexed isAllowed);
/**
* @dev Emitted when tokens are recovered from the contract.
* @param sender address that performed the recovery.
* @param amount of tokens recovered.
*/
event Recover(address indexed sender, uint256 amount);
/**
* @dev Allows a user to claim tokens or veNFT tokens based on a Merkle proof.
* @param inPureTokens_ Boolean indicating if the claim is in pure tokens.
* @param amount_ The amount to claim.
* @param withPermanentLock_ Whether the lock should be permanent.
* @param managedTokenIdForAttach_ The ID of the managed NFT to attach, if any. 0 for ignore
* @param proof_ The Merkle proof for the claim.
* @notice This function can only be called when the contract is not paused.
*/
function claim(
bool inPureTokens_,
uint256 amount_,
bool withPermanentLock_,
uint256 managedTokenIdForAttach_,
bytes32[] memory proof_
) external;
/**
* @dev Allows a claim operator to claim tokens on behalf of a target address.
* @param target_ The address of the user on whose behalf tokens are being claimed.
* @param inPureTokens_ Boolean indicating if the claim is in pure tokens.
* @param amount_ The amount to claim.
* @param withPermanentLock_ Whether the lock should be permanent.
* @param managedTokenIdForAttach_ The ID of the managed NFT to attach, if any. 0 for ignore
* @param proof_ The Merkle proof verifying the user's claim.
* @notice This function can only be called when the contract is not paused.
* @notice Reverts with `NotAllowedClaimOperator` if the caller is not an allowed claim operator.
* @notice Emits a {Claim} event.
*/
function claimFor(
address target_,
bool inPureTokens_,
uint256 amount_,
bool withPermanentLock_,
uint256 managedTokenIdForAttach_,
bytes32[] memory proof_
) external;
/**
* @dev Sets whether an address is allowed to operate claims on behalf of others.
* Can only be called by the owner.
* @param operator_ The address of the operator to set.
* @param isAllowed_ A boolean indicating whether the operator is allowed.
* @notice Emits a {SetIsAllowedClaimOperator} event.
*/
function setIsAllowedClaimOperator(address operator_, bool isAllowed_) external;
/**
* @dev Pauses the contract, preventing any further claims.
* Can only be called by the owner.
*/
function pause() external;
/**
* @dev Unpauses the contract, allowing claims to be made.
* Can only be called by the owner.
*/
function unpause() external;
/**
* @dev Sets the Merkle root for verifying claims.
* Can only be called by the owner when the contract is paused.
* @param merklRoot_ The new Merkle root.
*/
function setMerklRoot(bytes32 merklRoot_) external;
/**
* @dev Sets the pure tokens rate.
* Can only be called by the owner when the contract is paused.
* @param pureTokensRate_ The new pure tokens rate.
* @notice Emits a {SetPureTokensRate} event.
*/
function setPureTokensRate(uint256 pureTokensRate_) external;
/**
* @notice Allows the owner to recover tokens from the contract.
* @param amount_ The amount of tokens to be recovered.
* Transfers the specified amount of tokens to the owner's address.
*/
function recoverToken(uint256 amount_) external;
/**
* @dev Verifies if a provided proof is valid for a given user and amount.
* @param user_ The address of the user.
* @param amount_ The amount to be verified.
* @param proof_ The Merkle proof.
* @return True if the proof is valid, false otherwise.
*/
function isValidProof(address user_, uint256 amount_, bytes32[] memory proof_) external view returns (bool);
/**
* @dev Returns the address of the token contract.
*/
function token() external view returns (address);
/**
* @dev Returns the address of the Voting Escrow contract.
*/
function votingEscrow() external view returns (address);
/**
* @dev Rate for pure tokens.
*/
function pureTokensRate() external view returns (uint256);
/**
* @dev Returns the Merkle root used for verifying claims.
*/
function merklRoot() external view returns (bytes32);
/**
* @dev Returns the amount of tokens claimed by a user.
* @param user The address of the user.
* @return The amount of tokens claimed by the user.
*/
function userClaimed(address user) external view returns (uint256);
/**
* @dev Checks if an address is an allowed claim operator.
* @param operator_ The address to check.
* @return true if the operator is allowed, false otherwise.
*/
function isAllowedClaimOperator(address operator_) external view returns (bool);
/**
* @dev Calculates the equivalent amount in pure tokens based on the claim amount.
* @param claimAmount_ The claim amount for which to calculate the equivalent pure tokens.
* @return The calculated amount of pure tokens.
*/
function calculatePureTokensAmount(uint256 claimAmount_) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IVoter {
/**
* @notice Represents the state of a gauge.
* @param isGauge Indicates if the address is a gauge.
* @param isAlive Indicates if the gauge is active.
* @param internalBribe The address of the internal bribe contract.
* @param externalBribe The address of the external bribe contract.
* @param pool The address of the associated pool.
* @param claimable The amount of rewards claimable by the gauge.
* @param index The current index used for reward distribution calculations.
* @param lastDistributionTimestamp The last time rewards were distributed.
*/
struct GaugeState {
bool isGauge;
bool isAlive;
address internalBribe;
address externalBribe;
address pool;
uint256 claimable;
uint256 index;
uint256 lastDistributionTimestamp;
}
/**
* @notice Parameters for creating a veNFT through VotingEscrow.
* @param percentageToLock The percentage (in 18 decimals) of the claimed reward tokens to be locked.
* @param lockDuration The duration (in seconds) for which the tokens will be locked.
* @param to The address that will receive the veNFT.
* @param shouldBoosted Indicates whether the veNFT should have boosted properties.
* @param withPermanentLock Indicates if the lock should be permanent.
* @param managedTokenIdForAttach The ID of the managed veNFT token to which this lock will be attached.
*/
struct AggregateCreateLockParams {
uint256 percentageToLock;
uint256 lockDuration;
address to;
bool shouldBoosted;
bool withPermanentLock;
uint256 managedTokenIdForAttach;
}
/**
* @notice Parameters for claiming bribes using a specific tokenId.
* @param tokenId The token ID to claim bribes for.
* @param bribes The array of bribe contract addresses.
* @param tokens The array of arrays containing token addresses for each bribe.
*/
struct AggregateClaimBribesByTokenIdParams {
uint256 tokenId;
address[] bribes;
address[][] tokens;
}
/**
* @notice Parameters for claiming bribes.
* @param bribes The array of bribe contract addresses.
* @param tokens The array of arrays containing token addresses for each bribe.
*/
struct AggregateClaimBribesParams {
address[] bribes;
address[][] tokens;
}
/**
* @notice Parameters for claiming Merkl data.
* @param users The array of user addresses to claim for.
* @param tokens The array of token addresses.
* @param amounts The array of amounts to claim.
* @param proofs The array of arrays containing Merkle proofs.
*/
struct AggregateClaimMerklDataParams {
address[] users;
address[] tokens;
uint256[] amounts;
bytes32[][] proofs;
}
/**
* @notice Parameters for claiming VeFnx Merkl airdrop data.
* @param inPureTokens_ Boolean indicating if the claim is in pure tokens.
* @param amount The amount to claim.
* @param withPermanentLock_ Whether the lock should be permanent.
* @param managedTokenIdForAttach_ The ID of the managed NFT to attach, if any. 0 for ignore
* @param proofs The array of Merkle proofs.
*/
struct AggregateClaimVeFnxMerklAirdrop {
bool inPureTokens;
uint256 amount;
bool withPermanentLock;
uint256 managedTokenIdForAttach;
bytes32[] proofs;
}
/**
* @notice Emitted when a gauge is created.
* @param gauge The address of the created gauge.
* @param creator The address of the creator.
* @param internalBribe The address of the created internal bribe.
* @param externalBribe The address of the created external bribe.
* @param pool The address of the associated pool.
*/
event GaugeCreated(address indexed gauge, address creator, address internalBribe, address indexed externalBribe, address indexed pool);
/**
* @notice Emitted when a gauge is created.
* @param gauge The address of the created gauge.
* @param gaugeType Type identifier of the created gauge.
*/
event GaugeCreatedType(address indexed gauge, uint256 indexed gaugeType);
/**
* @notice Emitted when a gauge is killed.
* @param gauge The address of the killed gauge.
*/
event GaugeKilled(address indexed gauge);
/**
* @notice Emitted when a gauge is revived.
* @param gauge The address of the revived gauge.
*/
event GaugeRevived(address indexed gauge);
/**
* @dev Emitted when a user casts votes for multiple pools using a specific token.
*
* @param voter The address of the user who cast the votes.
* @param tokenId The ID of the token used for voting.
* @param epoch The epoch during which the votes were cast.
* @param pools An array of pool addresses that received votes.
* @param voteWeights An array representing the weight of votes allocated to each pool.
*
* Requirements:
* - `pools` and `voteWeights` arrays must have the same length.
*
* Note: The voting power represented in `voteWeights` is allocated across the specified `votedPools` for the given `epoch`.
* The `totalVotingPower` represents the cumulative voting power used in this vote.
*/
event VoteCast(
address indexed voter,
uint256 indexed tokenId,
uint256 indexed epoch,
address[] pools,
uint256[] voteWeights,
uint256 totalVotingPower
);
/**
* @dev Emitted when a user resets all votes for the current epoch.
*
* @param voter The address of the user who resets their votes.
* @param tokenId The ID of the token used for voting that is being reset.
* @param epoch The epoch during which the votes were reset.
* @param totalResetVotingPower The total voting power that was reset.
*
* Note: This event indicates that all previously cast votes for the specified `epoch` have been reset for the given `votingTokenId`.
* The `totalResetVotingPower` represents the cumulative voting power that was removed during the reset.
*/
event VoteReset(address indexed voter, uint256 indexed tokenId, uint256 indexed epoch, uint256 totalResetVotingPower);
/**
* @notice Emitted when rewards are notified for distribution.
* @param sender The address of the sender.
* @param reward The address of the reward token.
* @param amount The amount of rewards to distribute.
*/
event NotifyReward(address indexed sender, address indexed reward, uint256 amount);
/**
* @notice Emitted when rewards are distributed to a gauge.
* @param sender The address of the sender.
* @param gauge The address of the gauge receiving the rewards.
* @param amount The amount of rewards distributed.
*/
event DistributeReward(address indexed sender, address indexed gauge, uint256 amount);
/**
* @notice Emitted when the vote delay is updated.
* @param old The previous vote delay.
* @param latest The new vote delay.
*/
event SetVoteDelay(uint256 old, uint256 latest);
/**
* @notice Emitted when a contract address is updated.
* @param key The key representing the contract.
* @param value The new address of the contract.
*/
event UpdateAddress(string key, address indexed value);
/// @notice Event emitted when voting is paused or unpaused.
/// @dev Emits the current paused state of voting.
/// @param paused Indicates whether voting is paused (true) or unpaused (false).
event VotingPaused(bool indexed paused);
/**
* @notice Emitted when the distribution window duration is set or updated.
* @param duration New duration of the distribution window in seconds.
*/
event SetDistributionWindowDuration(uint256 indexed duration);
/**
* @notice Emitted when a token is attached to a managed NFT.
* @param tokenId ID of the user's token that is being attached.
* @param managedTokenId ID of the managed token to which the user's token is attached.
*/
event AttachToManagedNFT(uint256 indexed tokenId, uint256 indexed managedTokenId);
/**
* @notice Emitted when a token is detached from a managed NFT.
* @param tokenId ID of the user's token that is being detached.
*/
event DettachFromManagedNFT(uint256 indexed tokenId);
/**
* @notice Updates the address of a specified contract.
* @param key_ The key representing the contract.
* @param value_ The new address of the contract.
*/
function updateAddress(string memory key_, address value_) external;
/**
* @notice Sets the duration of the distribution window for voting.
* @param distributionWindowDuration_ The duration in seconds.
*/
function setDistributionWindowDuration(uint256 distributionWindowDuration_) external;
/**
* @notice Disables a gauge, preventing further rewards distribution.
* @param gauge_ The address of the gauge to be disabled.
*/
function killGauge(address gauge_) external;
/**
* @notice Revives a previously disabled gauge, allowing it to distribute rewards again.
* @param gauge_ The address of the gauge to be revived.
*/
function reviveGauge(address gauge_) external;
/**
* @notice Creates a new V2 gauge for a specified pool.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createV2Gauge(address pool_) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Creates a new V3 gauge for a specified pool.
* @param pool_ The address of the pool for which to create a gauge.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createV3Gauge(address pool_) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Creates a custom gauge with specified parameters.
* @param gauge_ The address of the custom gauge.
* @param pool_ The address of the pool for which to create a gauge.
* @param tokenA_ The address of token A in the pool.
* @param tokenB_ The address of token B in the pool.
* @param externalBribesName_ The name of the external bribe.
* @param internalBribesName_ The name of the internal bribe.
* @return gauge The address of the created gauge.
* @return internalBribe The address of the created internal bribe.
* @return externalBribe The address of the created external bribe.
*/
function createCustomGauge(
address gauge_,
address pool_,
address tokenA_,
address tokenB_,
string memory externalBribesName_,
string memory internalBribesName_
) external returns (address gauge, address internalBribe, address externalBribe);
/**
* @notice Notifies the contract of a reward amount to be distributed.
* @param amount_ The amount of rewards to distribute.
*/
function notifyRewardAmount(uint256 amount_) external;
/**
* @notice Distributes fees to a list of gauges.
* @param gauges_ An array of gauge addresses to distribute fees to.
*/
function distributeFees(address[] calldata gauges_) external;
/**
* @notice Distributes rewards to all pools managed by the contract.
*/
function distributeAll() external;
/**
* @notice Distributes rewards to a specified range of pools.
* @param start_ The starting index of the pool array.
* @param finish_ The ending index of the pool array.
*/
function distribute(uint256 start_, uint256 finish_) external;
/**
* @notice Distributes rewards to a specified list of gauges.
* @param gauges_ An array of gauge addresses to distribute rewards to.
*/
function distribute(address[] calldata gauges_) external;
/**
* @notice Resets the votes for a given NFT token ID.
* @param tokenId_ The token ID for which to reset votes.
*/
function reset(uint256 tokenId_) external;
/**
* @notice Updates the voting preferences for a given token ID.
* @param tokenId_ The token ID for which to update voting preferences.
*/
function poke(uint256 tokenId_) external;
/**
* @notice Casts votes for a given NFT token ID.
* @param tokenId_ The token ID for which to cast votes.
* @param poolsVotes_ An array of pool addresses to vote for.
* @param weights_ An array of weights corresponding to the pools.
*/
function vote(uint256 tokenId_, address[] calldata poolsVotes_, uint256[] calldata weights_) external;
/**
* @notice Claims rewards from multiple gauges.
* @param _gauges An array of gauge addresses to claim rewards from.
*/
function claimRewards(address[] memory _gauges) external;
/**
* @notice Claims bribes for a given NFT token ID from multiple bribe contracts.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
* @param tokenId_ The token ID for which to claim bribes.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens, uint256 tokenId_) external;
/**
* @notice Claims bribes from multiple bribe contracts.
* @param _bribes An array of bribe contract addresses to claim bribes from.
* @param _tokens An array of token arrays, specifying the tokens to claim.
*/
function claimBribes(address[] memory _bribes, address[][] memory _tokens) external;
/**
* @notice Handles the deposit of voting power to a managed NFT.
* @dev This function is called after tokens are deposited into the Voting Escrow contract for a managed NFT.
* Only callable by the Voting Escrow contract.
* @param tokenId_ The ID of the token that has received the deposit.
* @param managedTokenId_ The ID of the managed token receiving the voting power.
* @custom:error AccessDenied Thrown if the caller is not the Voting Escrow contract.
*/
function onDepositToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external;
/**
* @notice Attaches a tokenId to a managed tokenId.
* @param tokenId_ The user's tokenId to be attached.
* @param managedTokenId_ The managed tokenId to attach to.
*/
function attachToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external;
/**
* @notice Detaches a tokenId from its managed tokenId.
* @param tokenId_ The user's tokenId to be detached.
*/
function dettachFromManagedNFT(uint256 tokenId_) external;
/**
* @notice Checks if the provided address is a registered gauge.
* @param gauge_ The address of the gauge to check.
* @return True if the address is a registered gauge, false otherwise.
*/
function isGauge(address gauge_) external view returns (bool);
/**
* @notice Returns the state of a specific gauge.
* @param gauge_ The address of the gauge.
* @return GaugeState The current state of the specified gauge.
*/
function getGaugeState(address gauge_) external view returns (GaugeState memory);
/**
* @notice Checks if the specified gauge is alive (i.e., enabled for reward distribution).
* @param gauge_ The address of the gauge to check.
* @return True if the gauge is alive, false otherwise.
*/
function isAlive(address gauge_) external view returns (bool);
/**
* @notice Returns the pool address associated with a specified gauge.
* @param gauge_ The address of the gauge to query.
* @return The address of the pool associated with the specified gauge.
*/
function poolForGauge(address gauge_) external view returns (address);
/**
* @notice Returns the gauge address associated with a specified pool.
* @param pool_ The address of the pool to query.
* @return The address of the gauge associated with the specified pool.
*/
function poolToGauge(address pool_) external view returns (address);
/**
* @notice Returns the address of the Voting Escrow contract.
* @return The address of the Voting Escrow contract.
*/
function votingEscrow() external view returns (address);
/**
* @notice Returns the address of the Minter contract.
* @return The address of the Minter contract.
*/
function minter() external view returns (address);
/**
* @notice Returns the address of the V2 Pool Factory contract.
* @return The address of the V2 Pool Factory contract.
*/
function v2PoolFactory() external view returns (address);
/**
* @notice Returns the address of the V3 Pool Factory contract.
* @return The address of the V3 Pool Factory contract.
*/
function v3PoolFactory() external view returns (address);
/**
* @notice Returns the V2 pool address at a specific index.
* @param index The index of the V2 pool.
* @return The address of the V2 pool at the specified index.
*/
function v2Pools(uint256 index) external view returns (address);
/**
* @notice Returns the V3 pool address at a specific index.
* @param index The index of the V3 pool.
* @return The address of the V3 pool at the specified index.
*/
function v3Pools(uint256 index) external view returns (address);
/**
* @notice Returns the total number of pools, V2 pools, and V3 pools managed by the contract.
* @return totalCount The total number of pools.
* @return v2PoolsCount The total number of V2 pools.
* @return v3PoolsCount The total number of V3 pools.
*/
function poolsCounts() external view returns (uint256 totalCount, uint256 v2PoolsCount, uint256 v3PoolsCount);
/**
* @notice Returns the current epoch timestamp used for reward calculations.
* @return The current epoch timestamp.
*/
function epochTimestamp() external view returns (uint256);
/**
* @notice Returns the weight for a specific pool in a given epoch.
* @param timestamp The timestamp of the epoch.
* @param pool The address of the pool.
* @return The weight of the pool in the specified epoch.
*/
function weightsPerEpoch(uint256 timestamp, address pool) external view returns (uint256);
/**
* @notice Returns the vote weight of a specific NFT token ID for a given pool.
* @param tokenId The ID of the NFT.
* @param pool The address of the pool.
* @return The vote weight of the token for the specified pool.
*/
function votes(uint256 tokenId, address pool) external view returns (uint256);
/**
* @notice Returns the number of pools that an NFT token ID has voted for.
* @param tokenId The ID of the NFT.
* @return The number of pools the token has voted for.
*/
function poolVoteLength(uint256 tokenId) external view returns (uint256);
/**
* @notice Returns the pool address at a specific index for which the NFT token ID has voted.
* @param tokenId The ID of the NFT.
* @param index The index of the pool.
* @return The address of the pool at the specified index.
*/
function poolVote(uint256 tokenId, uint256 index) external view returns (address);
/**
* @notice Returns the last timestamp when an NFT token ID voted.
* @param tokenId The ID of the NFT.
* @return The timestamp of the last vote.
*/
function lastVotedTimestamps(uint256 tokenId) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
/**
* @title IVotingEscrow
* @notice Interface for Voting Escrow, allowing users to lock tokens in exchange for veNFTs that are used in governance and other systems.
*/
interface IVotingEscrow is IERC721Upgradeable {
/**
* @notice Enum representing the types of deposits that can be made.
* @dev Defines the context in which a deposit is made:
* - `DEPOSIT_FOR_TYPE`: Regular deposit for an existing lock.
* - `CREATE_LOCK_TYPE`: Creating a new lock.
* - `INCREASE_UNLOCK_TIME`: Increasing the unlock time for an existing lock.
* - `MERGE_TYPE`: Merging two locks together.
*/
enum DepositType {
DEPOSIT_FOR_TYPE,
CREATE_LOCK_TYPE,
INCREASE_UNLOCK_TIME,
MERGE_TYPE
}
/**
* @notice Structure representing the state of a token.
* @dev This includes information about the lock, voting status, attachment status,
* the block of the last transfer, and the index (epoch) of its latest checkpoint.
* @param locked The locked balance (amount + end timestamp + permanent status) of the token.
* @param isVoted Whether the token has been used to vote in the current epoch.
* @param isAttached Whether the token is attached to a managed NFT.
* @param lastTranferBlock The block number of the last transfer.
* @param pointEpoch The epoch (checkpoint index) for the token’s most recent voting power change.
*/
struct TokenState {
LockedBalance locked;
bool isVoted;
bool isAttached;
uint256 lastTranferBlock;
uint256 pointEpoch;
}
/**
* @notice Structure representing a locked balance.
* @dev Contains the amount locked, the end timestamp of the lock, and whether the lock is permanent.
* @param amount The amount of tokens locked (signed integer for slope calculations).
* @param end The timestamp when the lock ends (0 if permanently locked).
* @param isPermanentLocked Whether the lock is permanent (no unlock time).
*/
struct LockedBalance {
int128 amount;
uint256 end;
bool isPermanentLocked;
}
/**
* @notice Structure representing a point in time for calculating voting power.
* @dev Used for slope/bias math across epochs.
* @param bias The bias of the lock, representing the remaining voting power.
* @param slope The rate at which voting power (bias) decays over time.
* @param ts The timestamp of the checkpoint.
* @param blk The block number of the checkpoint.
* @param permanent The permanently locked amount at this checkpoint.
*/
struct Point {
int128 bias;
int128 slope; // -dweight / dt
uint256 ts;
uint256 blk; // block
int128 permanent;
}
/**
* @notice Emitted when a boost is applied to a token's lock.
* @param tokenId The ID of the token that received the boost.
* @param value The amount of tokens used as a boost.
*/
event Boost(uint256 indexed tokenId, uint256 value);
/**
* @notice Emitted when a deposit is made into a lock.
* @param provider The address of the entity making the deposit.
* @param tokenId The ID of the token associated with the deposit.
* @param value The amount of tokens deposited.
* @param locktime The time (timestamp) until which the lock is extended.
* @param deposit_type The type of deposit (see {DepositType}).
* @param ts The timestamp when the deposit was made.
*/
event Deposit(address indexed provider, uint256 tokenId, uint256 value, uint256 indexed locktime, DepositType deposit_type, uint256 ts);
/**
* @notice Emitted when tokens are deposited to an attached NFT.
* @param provider The address of the user making the deposit.
* @param tokenId The ID of the NFT receiving the deposit.
* @param managedTokenId The ID of the managed token receiving the voting power.
* @param value The amount of tokens deposited.
*/
event DepositToAttachedNFT(address indexed provider, uint256 tokenId, uint256 managedTokenId, uint256 value);
/**
* @notice Emitted when a withdrawal is made from a lock.
* @param provider The address of the entity making the withdrawal.
* @param tokenId The ID of the token associated with the withdrawal.
* @param value The amount of tokens withdrawn.
* @param ts The timestamp when the withdrawal occurred.
*/
event Withdraw(address indexed provider, uint256 tokenId, uint256 value, uint256 ts);
/**
* @notice Emitted when the merging process of two veNFT locks is initiated.
* @param tokenFromId The ID of the token being merged from.
* @param tokenToId The ID of the token being merged into.
*/
event MergeInit(uint256 tokenFromId, uint256 tokenToId);
/**
* @notice Emitted when two veNFT locks are successfully merged.
* @param provider The address of the entity initiating the merge.
* @param tokenIdFrom The ID of the token being merged from.
* @param tokenIdTo The ID of the token being merged into.
*/
event Merge(address indexed provider, uint256 tokenIdFrom, uint256 tokenIdTo);
/**
* @notice Emitted when the total supply of voting power changes.
* @param prevSupply The previous total supply of voting power.
* @param supply The new total supply of voting power.
*/
event Supply(uint256 prevSupply, uint256 supply);
/**
* @notice Emitted when an address associated with the contract is updated.
* @param key The key representing the contract being updated.
* @param value The new address of the contract.
*/
event UpdateAddress(string key, address indexed value);
/**
* @notice Emitted when a token is permanently locked by a user.
* @param sender The address of the user who initiated the lock.
* @param tokenId The ID of the token that has been permanently locked.
*/
event LockPermanent(address indexed sender, uint256 indexed tokenId);
/**
* @notice Emitted when a token is unlocked from a permanent lock state by a user.
* @param sender The address of the user who initiated the unlock.
* @param tokenId The ID of the token that has been unlocked from its permanent state.
*/
event UnlockPermanent(address indexed sender, uint256 indexed tokenId);
/**
* @notice Emitted when a veFNX NFT lock is burned and the underlying FNX is released for use in bribes.
* @param sender The address which initiated the burn-to-bribes operation.
* @param tokenId The identifier of the veFNX NFT that was burned.
* @param value The amount of FNX tokens released from the burned lock.
*/
event BurnToBribes(address indexed sender, uint256 indexed tokenId, uint256 value);
/**
* @notice Returns the address of the token used in voting escrow.
* @return The address of the token contract.
*/
function token() external view returns (address);
/**
* @notice Returns the address of the voter contract.
* @return The address of the voter.
*/
function voter() external view returns (address);
/**
* @notice Checks if the specified address is approved or the owner of the given token.
* @param sender The address to check.
* @param tokenId The ID of the token to check.
* @return True if `sender` is approved or the owner of `tokenId`, otherwise false.
*/
function isApprovedOrOwner(address sender, uint256 tokenId) external view returns (bool);
/**
* @notice Checks if a specific NFT token is transferable.
* @dev In the current implementation, this function always returns `true`,
* meaning the contract does not enforce non-transferability at code level.
* @param tokenId_ The ID of the NFT to check.
* @return bool Always returns true in the current version.
*/
function isTransferable(uint256 tokenId_) external view returns (bool);
/**
* @notice Retrieves the state of a specific NFT.
* @param tokenId_ The ID of the NFT to query.
* @return The current {TokenState} of the specified NFT.
*/
function getNftState(uint256 tokenId_) external view returns (TokenState memory);
/**
* @notice Returns the total supply of voting power at the current block timestamp.
* @return The total supply of voting power.
*/
function votingPowerTotalSupply() external view returns (uint256);
/**
* @notice Returns the balance of a veNFT at the current block timestamp.
* @dev Balance is determined by the lock’s slope and bias at this moment.
* @param tokenId_ The ID of the veNFT to query.
* @return The current voting power (balance) of the veNFT.
*/
function balanceOfNFT(uint256 tokenId_) external view returns (uint256);
/**
* @notice Returns the balance of a veNFT at the current block timestamp, ignoring ownership changes.
* @dev This function is similar to {balanceOfNFT} but does not zero out the balance
* if the token was transferred in the same block.
* @param tokenId_ The ID of the veNFT to query.
* @return The current voting power (balance) of the veNFT.
*/
function balanceOfNftIgnoreOwnershipChange(uint256 tokenId_) external view returns (uint256);
/**
* @notice Updates the address of a specified contract.
* @param key_ The key representing the contract.
* @param value_ The new address of the contract.
* @dev Reverts with `InvalidAddressKey` if the key does not match any known setting.
* Emits an {UpdateAddress} event on success.
*/
function updateAddress(string memory key_, address value_) external;
/**
* @notice Hooks the voting state for a specified NFT.
* @dev Only callable by the voter contract. Used to mark a veNFT as having voted or not.
* @param tokenId_ The ID of the NFT.
* @param state_ True if the NFT is now considered “voted,” false otherwise.
* @custom:error AccessDenied If called by any address other than the voter.
*/
function votingHook(uint256 tokenId_, bool state_) external;
/**
* @notice Creates a new lock for a specified recipient.
* @param amount_ The amount of tokens to lock.
* @param lockDuration_ The duration in seconds for which the tokens will be locked.
* @param to_ The address of the recipient who will receive the new veNFT.
* @param shouldBoosted_ Whether the deposit should attempt to get a veBoost.
* @param withPermanentLock_ Whether the lock should be created as a permanent lock.
* @param managedTokenIdForAttach_ (Optional) The ID of the managed NFT to attach. Pass 0 to ignore.
* @return The ID of the newly created veNFT.
* @dev Reverts with `InvalidLockDuration` if lockDuration_ is 0 or too large.
* Reverts with `ValueZero` if amount_ is 0.
* Emits a {Deposit} event on success.
*/
function createLockFor(
uint256 amount_,
uint256 lockDuration_,
address to_,
bool shouldBoosted_,
bool withPermanentLock_,
uint256 managedTokenIdForAttach_
) external returns (uint256);
/**
* @notice Deposits tokens for a specific NFT, increasing its locked balance.
* @param tokenId_ The ID of the veNFT to top up.
* @param amount_ The amount of tokens to deposit.
* @param shouldBoosted_ Whether this deposit should attempt to get a veBoost.
* @param withPermanentLock_ Whether to apply a permanent lock alongside the deposit.
* @dev Reverts with `ValueZero` if amount_ is 0.
* Emits a {Deposit} event upon success.
*/
function depositFor(uint256 tokenId_, uint256 amount_, bool shouldBoosted_, bool withPermanentLock_) external;
/**
* @notice Increases the unlock time for an existing lock.
* @param tokenId_ The ID of the veNFT to extend.
* @param lockDuration_ The additional duration in seconds to add to the current unlock time.
* @dev Reverts with `InvalidLockDuration` if the new unlock time is invalid.
* Reverts with `AccessDenied` if the caller is not the owner or approved.
* Emits a {Deposit} event with the deposit type set to {INCREASE_UNLOCK_TIME}.
*/
function increase_unlock_time(uint256 tokenId_, uint256 lockDuration_) external;
/**
* @notice Deposits tokens and extends the lock duration for a veNFT in one call.
* @dev This may trigger veBoost if conditions are met.
* @param tokenId_ The ID of the veNFT.
* @param amount_ The amount of tokens to deposit.
* @param lockDuration_ The duration in seconds to add to the current unlock time.
* Emits one {Deposit} event for the deposit itself
* and another {Deposit} event for the unlock time increase.
*/
function depositWithIncreaseUnlockTime(uint256 tokenId_, uint256 amount_, uint256 lockDuration_) external;
/**
* @notice Deposits tokens directly into a veNFT that is attached to a managed NFT.
* @dev This updates the locked balance on the managed NFT, adjusts total supply,
* and emits {DepositToAttachedNFT} and {Supply} events.
* @param tokenId_ The ID of the attached veNFT.
* @param amount_ The amount of tokens to deposit.
* @custom:error NotManagedNft if the managed token ID is invalid or not recognized.
*/
function depositToAttachedNFT(uint256 tokenId_, uint256 amount_) external;
/**
* @notice Withdraws tokens from an expired lock (non-permanent).
* @param tokenId_ The ID of the veNFT to withdraw from.
* @dev Reverts with `AccessDenied` if caller is not owner or approved.
* Reverts with `TokenNoExpired` if the lock is not yet expired.
* Reverts with `PermanentLocked` if the lock is permanent.
* Emits a {Withdraw} event and a {Supply} event.
*/
function withdraw(uint256 tokenId_) external;
/**
* @notice Merges one veNFT (tokenFromId_) into another (tokenToId_).
* @param tokenFromId_ The ID of the source veNFT being merged.
* @param tokenToId_ The ID of the target veNFT receiving the locked tokens.
* @dev Reverts with `MergeTokenIdsTheSame` if both IDs are the same.
* Reverts with `AccessDenied` if the caller isn't owner or approved for both IDs.
* Emits a {MergeInit} event at the start, and a {Merge} event upon completion.
* Also emits a {Deposit} event reflecting the updated lock in the target token.
*/
function merge(uint256 tokenFromId_, uint256 tokenToId_) external;
/**
* @notice Permanently locks a veNFT.
* @param tokenId_ The ID of the veNFT to be permanently locked.
* @dev Reverts with `AccessDenied` if caller isn't owner or approved.
* Reverts with `TokenAttached` if the token is attached to a managed NFT.
* Reverts with `PermanentLocked` if the token already permanent lcoked
* Emits {LockPermanent} on success.
*/
function lockPermanent(uint256 tokenId_) external;
/**
* @notice Unlocks a permanently locked veNFT, reverting it to a temporary lock.
* @param tokenId_ The ID of the veNFT to unlock.
* @dev Reverts with `AccessDenied` if caller isn't owner or approved.
* Reverts with `TokenAttached` if the token is attached.
* Reverts with `NotPermanentLocked` if the lock isn't actually permanent.
* Emits {UnlockPermanent} on success.
*/
function unlockPermanent(uint256 tokenId_) external;
/**
* @notice Creates a new managed NFT for a given recipient.
* @param recipient_ The address that will receive the newly created managed NFT.
* @return The ID of the newly created managed NFT.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
*/
function createManagedNFT(address recipient_) external returns (uint256);
/**
* @notice Attaches a veNFT (user’s token) to a managed NFT, combining their locked balances.
* @param tokenId_ The ID of the user’s veNFT being attached.
* @param managedTokenId_ The ID of the managed NFT.
* @return The amount of tokens locked during the attachment.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
* Reverts with `ZeroVotingPower` if the user’s token has zero voting power.
* Reverts with `NotManagedNft` if the target is not recognized as a managed NFT.
*/
function onAttachToManagedNFT(uint256 tokenId_, uint256 managedTokenId_) external returns (uint256);
/**
* @notice Detaches a veNFT from a managed NFT.
* @param tokenId_ The ID of the user’s veNFT being detached.
* @param managedTokenId_ The ID of the managed NFT from which it’s being detached.
* @param newBalance_ The new locked balance the veNFT will hold after detachment.
* @dev Reverts with `AccessDenied` if caller is not the managed NFT manager.
* Reverts with `NotManagedNft` if the target is not recognized as a managed NFT.
*/
function onDettachFromManagedNFT(uint256 tokenId_, uint256 managedTokenId_, uint256 newBalance_) external;
/**
* @notice Burns a veFNX NFT to reclaim the underlying FNX tokens for use in bribes.
* @dev Must be called by `customBribeRewardRouter`.
* The token must not be permanently locked or attached.
* Also resets any votes before burning.
* Emits a {BurnToBribes} event on successful burn.
* @param tokenId_ The ID of the veFNX NFT to burn.
*/
function burnToBribes(uint256 tokenId_) external;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @notice Reverts when access is denied for the operation. */ error AccessDenied(); /** * @notice Reverts when an invalid address key is provided. */ error InvalidAddressKey(); /** * @notice Reverts when the vote delay has already been set. */ error VoteDelayAlreadySet(); /** * @notice Reverts when the provided vote delay exceeds the maximum allowed. */ error VoteDelayTooBig(); /** * @notice Reverts when an operation is attempted on a gauge that has already been killed. */ error GaugeAlreadyKilled(); /** * @notice Reverts when an operation is attempted on a gauge that is not currently killed. */ error GaugeNotKilled(); /** * @notice Reverts when an operation is attempted on a pool that was not created by the factory. */ error PoolNotCreatedByFactory(); /** * @notice Reverts when an attempt is made to create a gauge for a pool that already has one. */ error GaugeForPoolAlreadyExists(); /** * @notice Reverts when a voting operation is attempted without a prior reset. */ error NoResetBefore(); /** * @notice Reverts when the calculated vote power for a pool is zero. */ error ZeroPowerForPool(); /** * @notice Reverts when the required delay period for voting has not passed. */ error VoteDelay(); /** * @notice Reverts when the lengths of provided arrays do not match. */ error ArrayLengthMismatch(); /** * @notice Reverts when an operation is attempted on a disabled managed NFT. */ error DisabledManagedNft(); /** * @notice Reverts when the operation is attempted outside the allowed distribution window. */ error DistributionWindow(); /** * @notice Reverts when the try create gauge for pool without setup fees vault. */ error PoolNotInitialized();
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IPairFactory {
event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint);
event SetPaused(bool state);
event SetCommunityVaultFactory(address indexed communityVaultFactory);
event SetIsPublicPoolCreationMode(bool mode);
event SetProtocolFee(uint256 fee);
event SetCustomProtocolFee(address indexed pair, uint256 fee);
event SetCustomFee(address indexed pair, uint256 fee);
event SetFee(bool stable, uint256 fee);
/**
* @dev Emitted when the rebasing tokens governor address is set.
*
* @param oldRebasingTokensGovernor The previous address of the rebasing tokens governor.
* @param newRebasingTokensGovernor The new address of the rebasing tokens governor.
*/
event SetRebasingTokensGovernor(address indexed oldRebasingTokensGovernor, address indexed newRebasingTokensGovernor);
error IncorrcectFee();
error IncorrectPair();
error IdenticalAddress();
error PairExist();
function implementation() external view returns (address);
function PAIRS_ADMINISTRATOR_ROLE() external view returns (bytes32);
function FEES_MANAGER_ROLE() external view returns (bytes32);
function PAIRS_CREATOR_ROLE() external view returns (bytes32);
function hasRole(bytes32 role, address user) external view returns (bool);
function allPairsLength() external view returns (uint);
function isPair(address pair) external view returns (bool);
function allPairs(uint index) external view returns (address);
function getPair(address tokenA, address token, bool stable) external view returns (address);
function createPair(address tokenA, address tokenB, bool stable) external returns (address pair);
function pairs() external view returns (address[] memory);
function getFee(address pair_, bool stable_) external view returns (uint256);
function getHookTarget(address pair_) external view returns (address);
function getProtocolFee(address pair_) external view returns (uint256);
function isPaused() external view returns (bool);
function isPublicPoolCreationMode() external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IGauge {
function TOKEN() external view returns (address);
function notifyRewardAmount(address token, uint amount) external;
function getReward(address account) external;
function earned(address account) external view returns (uint256);
function periodFinish() external view returns (uint256);
function rewardRate() external view returns (uint256);
function claimFees() external returns (uint claimed0, uint claimed1);
function balanceOf(address _account) external view returns (uint);
function totalSupply() external view returns (uint);
function setDistribution(address _distro) external;
function activateEmergencyMode() external;
function stopEmergencyMode() external;
function setInternalBribe(address intbribe) external;
function setGaugeRewarder(address _gr) external;
function setFeeVault(address _feeVault) external;
function initialize(
address _governor,
address _rewardToken,
address _ve,
address _token,
address _distribution,
address _internal_bribe,
address _external_bribe,
bool _isToMerkleDistributor,
address _merklGaugeMiddleman,
address _feeVault
) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IGaugeFactory {
event GaugeImplementationChanged(address _oldGaugeImplementation, address _newGaugeImplementation);
/**
* @dev Emitted when the default Blast governor address for new bribes is updated.
*
* @param oldDefaultBlastGovernor The address of the previous default Blast governor.
* @param newDefaultBlastGovernor The address of the new default Blast governor that has been set.
*/
event SetDefaultBlastGovernor(address indexed oldDefaultBlastGovernor, address indexed newDefaultBlastGovernor);
function createGauge(
address _rewardToken,
address _ve,
address _token,
address _distribution,
address _internal_bribe,
address _external_bribe,
bool _isDistributeEmissionToMerkle,
address _feeVault
) external returns (address);
function gaugeImplementation() external view returns (address impl);
function merklGaugeMiddleman() external view returns (address);
function gaugeOwner() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.19;
import {IBlastFull, YieldMode, GasMode} from "./interfaces/IBlastFull.sol";
import {IBlastGovernor} from "./interfaces/IBlastGovernor.sol";
/**
* @title Blast Governor Claiamble Setup
* @dev Abstract contract for setting up a governor in the Blast ecosystem.
* This contract provides an initialization function to configure a governor address
* for the Blast protocol, utilizing the `IBlast` interface.
*/
abstract contract BlastGovernorClaimableSetup {
/// @dev Error thrown when an operation involves a zero address where a valid address is required.
error AddressZero();
/**
* @dev Initializes the governor and claimable configuration for the Blast protocol.
* This internal function is meant to be called in the initialization process
* of a derived contract that sets up governance.
*
* @param blastGovernor_ The address of the governor to be configured in the Blast protocol.
* Must be a non-zero address.
*/
function __BlastGovernorClaimableSetup_init(address blastGovernor_) internal {
if (blastGovernor_ == address(0)) {
revert AddressZero();
}
IBlastFull(0x4300000000000000000000000000000000000002).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, blastGovernor_);
if (blastGovernor_.code.length > 0) {
IBlastGovernor(blastGovernor_).addGasHolder(address(this));
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title IBlastFull Interface
* @dev Interface for interacting with the Blast protocol, specifically for configuring
* governance settings. This interface abstracts the function to set up a governor
* within the Blast ecosystem.
*/
enum GasMode {
VOID,
CLAIMABLE
}
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
interface IBlastFull {
// configure
function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256);
// NOTE: can be off by 1 bip
function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(
address contractAddress
) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
function isGovernor(address) external view returns (bool);
function governorMap(address) external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity =0.8.19;
import {GasMode} from "./IBlastFull.sol";
/**
* @title IBlastGovernor
* @dev Interface for the BlastGovernor contract.
*/
interface IBlastGovernor {
/**
* @dev Structure representing gas parameters.
* @param contractAddress Address of the gas holder contract.
* @param etherSeconds Accumulated ether seconds.
* @param etherBalance Ether balance.
* @param lastUpdated Timestamp of the last update.
* @param gasMode Current gas mode.
*/
struct GasParamsResult {
address contractAddress;
uint256 etherSeconds;
uint256 etherBalance;
uint256 lastUpdated;
GasMode gasMode;
}
/**
* @dev Emitted when a gas holder is added.
* @param contractAddress The address of the added gas holder contract.
*/
event AddGasHolder(address indexed contractAddress);
/**
* @dev Emitted when gas is claimed.
* @param caller The address of the caller who initiated the claim.
* @param recipient The address of the recipient who receives the claimed gas.
* @param gasHolders The addresses of the gas holders from which gas was claimed.
* @param totalClaimedAmount The total amount of gas claimed.
*/
event ClaimGas(address indexed caller, address indexed recipient, address[] gasHolders, uint256 totalClaimedAmount);
/**
* @notice Adds a gas holder.
* @dev Adds a contract to the list of gas holders.
* @param contractAddress_ The address of the gas holder contract.
*/
function addGasHolder(address contractAddress_) external;
/**
* @notice Claims all gas for a recipient within the specified range.
* @param recipient_ The address of the recipient.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimAllGas(address recipient_, uint256 offset_, uint256 limit_) external returns (uint256 totalClaimedGas);
/**
* @notice Claims all gas for a recipient from specified gas holders.
* @param recipient_ The address of the recipient.
* @param holders_ The addresses of the gas holders.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimAllGasFromSpecifiedGasHolders(address recipient_, address[] memory holders_) external returns (uint256 totalClaimedGas);
/**
* @notice Claims gas at minimum claim rate for a recipient within the specified range.
* @param recipient_ The address of the recipient.
* @param minClaimRateBips_ The minimum claim rate in basis points.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimGasAtMinClaimRate(
address recipient_,
uint256 minClaimRateBips_,
uint256 offset_,
uint256 limit_
) external returns (uint256 totalClaimedGas);
/**
* @notice Claims gas at minimum claim rate for a recipient from specified gas holders.
* @param recipient_ The address of the recipient.
* @param minClaimRateBips_ The minimum claim rate in basis points.
* @param holders_ The addresses of the gas holders.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimGasAtMinClaimRateFromSpecifiedGasHolders(
address recipient_,
uint256 minClaimRateBips_,
address[] memory holders_
) external returns (uint256 totalClaimedGas);
/**
* @notice Claims maximum gas for a recipient within the specified range.
* @param recipient_ The address of the recipient.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimMaxGas(address recipient_, uint256 offset_, uint256 limit_) external returns (uint256 totalClaimedGas);
/**
* @notice Claims maximum gas for a recipient from specified gas holders.
* @param recipient_ The address of the recipient.
* @param holders_ The addresses of the gas holders.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimMaxGasFromSpecifiedGasHolders(address recipient_, address[] memory holders_) external returns (uint256 totalClaimedGas);
/**
* @notice Claims a specific amount of gas for a recipient within the specified range.
* @param recipient_ The address of the recipient.
* @param gasToClaim_ The amount of gas to claim.
* @param gasSecondsToConsume_ The amount of gas seconds to consume.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimGas(
address recipient_,
uint256 gasToClaim_,
uint256 gasSecondsToConsume_,
uint256 offset_,
uint256 limit_
) external returns (uint256 totalClaimedGas);
/**
* @notice Claims a specific amount of gas for a recipient from specified gas holders.
* @param recipient_ The address of the recipient.
* @param gasToClaim_ The amount of gas to claim.
* @param gasSecondsToConsume_ The amount of gas seconds to consume.
* @param holders_ The addresses of the gas holders.
* @return totalClaimedGas The total amount of gas claimed.
*/
function claimGasFromSpecifiedGasHolders(
address recipient_,
uint256 gasToClaim_,
uint256 gasSecondsToConsume_,
address[] memory holders_
) external returns (uint256 totalClaimedGas);
/**
* @notice Reads gas parameters within the specified range.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return gasHoldersParams The gas parameters of the gas holders.
*/
function readGasParams(uint256 offset_, uint256 limit_) external view returns (GasParamsResult[] memory gasHoldersParams);
/**
* @notice Reads gas parameters from specified gas holders.
* @param holders_ The addresses of the gas holders.
* @return gasHoldersParams The gas parameters of the gas holders.
*/
function readGasParamsFromSpecifiedGasHolders(
address[] memory holders_
) external view returns (GasParamsResult[] memory gasHoldersParams);
/**
* @notice Checks if a contract is a registered gas holder.
* @param contractAddress_ The address of the contract.
* @return isRegistered Whether the contract is a registered gas holder.
*/
function isRegisteredGasHolder(address contractAddress_) external view returns (bool isRegistered);
/**
* @notice Lists gas holders within the specified range.
* @param offset_ The offset to start from.
* @param limit_ The maximum number of gas holders to process.
* @return gasHolders The addresses of the gas holders.
*/
function listGasHolders(uint256 offset_, uint256 limit_) external view returns (address[] memory gasHolders);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMerklDistributor {
function claim(address[] calldata users, address[] calldata tokens, uint256[] calldata amounts, bytes32[][] calldata proofs) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IPairIntegrationInfo {
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice The contract to which community fees are transferred
/// @return communityVaultAddress The communityVault address
function communityVault() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/**
* @title Interface for Managed NFT Manager
* @dev Defines the functions and events for managing NFTs, including attaching/detaching to strategies, authorization, and administrative checks.
*/
interface IManagedNFTManager {
/**
* @dev Emitted when the disabled state of a managed NFT is toggled.
* @param sender The address that triggered the state change.
* @param tokenId The ID of the managed NFT affected.
* @param isDisable True if the NFT is now disabled, false if it is enabled.
*/
event ToggleDisableManagedNFT(address indexed sender, uint256 indexed tokenId, bool indexed isDisable);
/**
* @dev Emitted when a new managed NFT is created and attached to a strategy.
* @param sender The address that performed the creation.
* @param strategy The address of the strategy to which the NFT is attached.
* @param tokenId The ID of the newly created managed NFT.
*/
event CreateManagedNFT(address indexed sender, address indexed strategy, uint256 indexed tokenId);
/**
* @dev Emitted when an NFT is whitelisted or removed from the whitelist.
* @param tokenId The ID of the NFT being modified.
* @param isWhitelisted True if the NFT is being whitelisted, false if it is being removed from the whitelist.
*/
event SetWhitelistedNFT(uint256 indexed tokenId, bool indexed isWhitelisted);
/**
* @dev Emitted when an authorized user is set for a managed NFT.
* @param managedTokenId The ID of the managed NFT.
* @param authorizedUser The address being authorized.
*/
event SetAuthorizedUser(uint256 indexed managedTokenId, address authorizedUser);
/**
* @notice Checks if a managed NFT is currently disabled.
* @param managedTokenId_ The ID of the managed NFT.
* @return True if the managed NFT is disabled, false otherwise.
*/
function isDisabledNFT(uint256 managedTokenId_) external view returns (bool);
/**
* @notice Determines if a token ID is recognized as a managed NFT within the system.
* @param managedTokenId_ The ID of the token to check.
* @return True if the token is a managed NFT, false otherwise.
*/
function isManagedNFT(uint256 managedTokenId_) external view returns (bool);
/**
* @notice Checks if an NFT is whitelisted within the management system.
* @param tokenId_ The ID of the NFT to check.
* @return True if the NFT is whitelisted, false otherwise.
*/
function isWhitelistedNFT(uint256 tokenId_) external view returns (bool);
/**
* @notice Verifies if a user's NFT is attached to any managed NFT.
* @param tokenId_ The ID of the user's NFT.
* @return True if the NFT is attached, false otherwise.
*/
function isAttachedNFT(uint256 tokenId_) external view returns (bool);
/**
* @notice Checks if a given account is an administrator of the managed NFT system.
* @param account_ The address to check.
* @return True if the address is an admin, false otherwise.
*/
function isAdmin(address account_) external view returns (bool);
/**
* @notice Retrieves the managed token ID that a user's NFT is attached to.
* @param tokenId_ The ID of the user's NFT.
* @return The ID of the managed token to which the NFT is attached.
*/
function getAttachedManagedTokenId(uint256 tokenId_) external view returns (uint256);
/**
* @notice Address of the Voting Escrow contract managing voting and staking mechanisms.
*/
function votingEscrow() external view returns (address);
/**
* @notice Address of the Voter contract responsible for handling governance actions related to managed NFTs.
*/
function voter() external view returns (address);
/**
* @notice Verifies if a given address is authorized to manage a specific managed NFT.
* @param managedTokenId_ The ID of the managed NFT.
* @param account_ The address to verify.
* @return True if the address is authorized, false otherwise.
*/
function isAuthorized(uint256 managedTokenId_, address account_) external view returns (bool);
/**
* @notice Assigns an authorized user for a managed NFT.
* @param managedTokenId_ The ID of the managed NFT.
* @param authorizedUser_ The address to authorize.
*/
function setAuthorizedUser(uint256 managedTokenId_, address authorizedUser_) external;
/**
* @notice Creates a managed NFT and attaches it to a strategy
* @param strategy_ The strategy to which the managed NFT will be attached
*/
function createManagedNFT(address strategy_) external returns (uint256 managedTokenId);
/**
* @notice Toggles the disabled state of a managed NFT
* @param managedTokenId_ The ID of the managed token to toggle
* @dev Enables or disables a managed token to control its operational status, with an event emitted for state change.
*/
function toggleDisableManagedNFT(uint256 managedTokenId_) external;
/**
* @notice Attaches a user's NFT to a managed NFT, enabling it within a specific strategy.
* @param tokenId_ The user's NFT token ID.
* @param managedTokenId The managed NFT token ID.
*/
function onAttachToManagedNFT(uint256 tokenId_, uint256 managedTokenId) external;
/**
* @notice Detaches a user's NFT from a managed NFT, disabling it within the strategy.
* @param tokenId_ The user's NFT token ID.
*/
function onDettachFromManagedNFT(uint256 tokenId_) external;
/**
* @notice Handles the deposit of tokens to an NFT attached to a managed token.
* @dev Called by the Voting Escrow contract when tokens are deposited to an NFT that is attached to a managed NFT.
* The function verifies the token is attached, checks if it is disabled, and updates the token's state.
* @param tokenId_ The token ID of the user's NFT.
* @param amount_ The amount of tokens to deposit.
* @custom:error IncorrectUserNFT Thrown if the provided token ID is not attached or if it is a managed token itself.
* @custom:error ManagedNFTIsDisabled Thrown if the managed token is currently disabled.
*/
function onDepositToAttachedNFT(uint256 tokenId_, uint256 amount_) external;
}{
"evmVersion": "paris",
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "none"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"blastGovernor_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"inputs":[],"name":"AddressZero","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"DisableDuringVotingPaused","type":"error"},{"inputs":[],"name":"DisabledManagedNft","type":"error"},{"inputs":[],"name":"DistributionWindow","type":"error"},{"inputs":[],"name":"GaugeAlreadyKilled","type":"error"},{"inputs":[],"name":"GaugeForPoolAlreadyExists","type":"error"},{"inputs":[],"name":"GaugeNotKilled","type":"error"},{"inputs":[],"name":"InvalidAddressKey","type":"error"},{"inputs":[],"name":"InvalidPercentageToLock","type":"error"},{"inputs":[],"name":"NoResetBefore","type":"error"},{"inputs":[],"name":"PoolNotCreatedByFactory","type":"error"},{"inputs":[],"name":"PoolNotInitialized","type":"error"},{"inputs":[],"name":"VoteDelay","type":"error"},{"inputs":[],"name":"ZeroPowerForPool","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"managedTokenId","type":"uint256"}],"name":"AttachToManagedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"DettachFromManagedNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DistributeReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"address","name":"internalBribe","type":"address"},{"indexed":true,"internalType":"address","name":"externalBribe","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"GaugeCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":true,"internalType":"uint256","name":"gaugeType","type":"uint256"}],"name":"GaugeCreatedType","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"GaugeKilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"GaugeRevived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NotifyReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"SetDistributionWindowDuration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"old","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"latest","type":"uint256"}],"name":"SetVoteDelay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"key","type":"string"},{"indexed":true,"internalType":"address","name":"value","type":"address"}],"name":"UpdateAddress","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"pools","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"voteWeights","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"totalVotingPower","type":"uint256"}],"name":"VoteCast","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalResetVotingPower","type":"uint256"}],"name":"VoteReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"paused","type":"bool"}],"name":"VotingPaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"gauges_","type":"address[]"},{"components":[{"internalType":"address[]","name":"bribes","type":"address[]"},{"internalType":"address[][]","name":"tokens","type":"address[][]"}],"internalType":"struct IVoter.AggregateClaimBribesParams","name":"bribes_","type":"tuple"},{"components":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address[]","name":"bribes","type":"address[]"},{"internalType":"address[][]","name":"tokens","type":"address[][]"}],"internalType":"struct IVoter.AggregateClaimBribesByTokenIdParams","name":"bribesByTokenId_","type":"tuple"},{"components":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes32[][]","name":"proofs","type":"bytes32[][]"}],"internalType":"struct IVoter.AggregateClaimMerklDataParams","name":"merkl_","type":"tuple"},{"components":[{"internalType":"bool","name":"inPureTokens","type":"bool"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"},{"internalType":"bytes32[]","name":"proofs","type":"bytes32[]"}],"internalType":"struct IVoter.AggregateClaimVeFnxMerklAirdrop","name":"splitMerklAidrop_","type":"tuple"},{"components":[{"internalType":"uint256","name":"percentageToLock","type":"uint256"},{"internalType":"uint256","name":"lockDuration","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"shouldBoosted","type":"bool"},{"internalType":"bool","name":"withPermanentLock","type":"bool"},{"internalType":"uint256","name":"managedTokenIdForAttach","type":"uint256"}],"internalType":"struct IVoter.AggregateCreateLockParams","name":"aggregateCreateLock_","type":"tuple"}],"name":"aggregateClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"uint256","name":"managedTokenId_","type":"uint256"}],"name":"attachToManagedNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bribeFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bribes","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_bribes","type":"address[]"},{"internalType":"address[][]","name":"_tokens","type":"address[][]"}],"name":"claimBribes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_gauges","type":"address[]"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"},{"internalType":"address","name":"pool_","type":"address"},{"internalType":"address","name":"tokenA_","type":"address"},{"internalType":"address","name":"tokenB_","type":"address"},{"internalType":"string","name":"externalBribesName_","type":"string"},{"internalType":"string","name":"internalBribesName_","type":"string"}],"name":"createCustomGauge","outputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"internalBribe","type":"address"},{"internalType":"address","name":"externalBribe","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool_","type":"address"}],"name":"createV2Gauge","outputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"internalBribe","type":"address"},{"internalType":"address","name":"externalBribe","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool_","type":"address"}],"name":"createV3Gauge","outputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"internalBribe","type":"address"},{"internalType":"address","name":"externalBribe","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"dettachFromManagedNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"gauges_","type":"address[]"}],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"start_","type":"uint256"},{"internalType":"uint256","name":"finish_","type":"uint256"}],"name":"distribute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributeAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"gauges_","type":"address[]"}],"name":"distributeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributionWindowDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"epochTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"gaugesState","outputs":[{"internalType":"bool","name":"isGauge","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"},{"internalType":"address","name":"internalBribe","type":"address"},{"internalType":"address","name":"externalBribe","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"lastDistributionTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"getGaugeState","outputs":[{"components":[{"internalType":"bool","name":"isGauge","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"},{"internalType":"address","name":"internalBribe","type":"address"},{"internalType":"address","name":"externalBribe","type":"address"},{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"claimable","type":"uint256"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"lastDistributionTimestamp","type":"uint256"}],"internalType":"struct IVoter.GaugeState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"blastGovernor_","type":"address"},{"internalType":"address","name":"votingEscrow_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"isAlive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"isGauge","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"killGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"lastVotedTimestamps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managedNFTManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"merklDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"managedTokenId_","type":"uint256"}],"name":"onDepositToManagedNFT","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"poke","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"poolForGauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"poolToGauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"poolVoteLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolsCounts","outputs":[{"internalType":"uint256","name":"totalCount","type":"uint256"},{"internalType":"uint256","name":"v2PoolsCount","type":"uint256"},{"internalType":"uint256","name":"v3PoolsCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"reset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge_","type":"address"}],"name":"reviveGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionWindowDuration_","type":"uint256"}],"name":"setDistributionWindowDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isPaused_","type":"bool"}],"name":"setVotingPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"totalWeightsPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"key_","type":"string"},{"internalType":"address","name":"value_","type":"address"}],"name":"updateAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"v2GaugeFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v2PoolFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"v2Pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v3GaugeFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"v3PoolFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"v3Pools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veFnxMerklAidrop","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"address[]","name":"poolsVotes_","type":"address[]"},{"internalType":"uint256[]","name":"weights_","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voteDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"votes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingEscrow","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"address","name":"pool","type":"address"}],"name":"weightsPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080346200022c57601f62005a2138819003918201601f191683019260009290916001600160401b0385118386101762000218578160209284926040978852833981010312620001db57516001600160a01b03811690818103620002145781156200020357734300000000000000000000000000000000000002803b15620001ff57838091606487518094819363c8992e6160e01b835260026004840152600160248401528860448401525af18015620001f557620001df575b503b6200017a575b50805460ff8160081c16620001265760ff80821603620000eb575b82516157c590816200025c8239f35b60ff908119161790557f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024986020825160ff8152a13880620000dc565b825162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608490fd5b803b15620001db5781809160248551809481936305573b1760e31b83523060048401525af18015620001d157908291620001b6575b50620000c1565b620001c19062000231565b620001ce578038620001af565b80fd5b83513d84823e3d90fd5b5080fd5b620001ed9093919362000231565b9138620000b9565b85513d86823e3d90fd5b8380fd5b8351639fabe1c160e01b8152600490fd5b8280fd5b634e487b7160e01b84526041600452602484fd5b600080fd5b6001600160401b0381116200024557604052565b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001357600080fd5b60009060e08235811c91826301ffc9a7146139665750816306d6a1b21461392657816307546172146138fd57816309ccce12146138d357816312dd72001461372d5781631703e5f9146136eb5781631e853c26146136cd578163248a9ca3146136a0578163292fc979146136825781632986c0e5146136645781632f2ff15d146135ac5781632fa4abea14613583578163310bd74b146134c257816332145f901461337657816336568abe146132e3578163396d79d0146132875781633c6b16ab146131a45781633d8534421461317b5781633e21750a14612da2578163436596c414612cfb578163485cc955146129d65781634bf002ab146121075781634f2bfe5b146120de578163537f95ff146120b55781635534d8f91461208b57816355e5a30814611e5a5781636103ed2e14611e375781636138889b14611d6e57816364279c4b14611d275781636447e7da14611cfd5781637625391a14611c3f5781637715ee7514611aea57816377f11e4614611abb5781637ac09bf7146118a45781637d55c17a146118105781637f246f671461179f5781638ec6383e146113b357816391d1485414611367578163992a79331461123b5781639f06247b146111a7578163a217fddf1461118b578163a86a366d14611159578163aa3f22b81461111d578163aa79979b146110de578163ab138846146110b5578163ac4afa3814611071578163ae66c9ea1461104e578163af3b629a14611025578163c2b79e9814610fd2578163c527ee1f14610e74578163ca82240d14610d86578163cea033d014610b9a578163d23254b414610b53578163d547741f14610b15578163d9f3416f14610ab9578163df0915ea14610a71578163e839663b1461095957508063eb4a78e014610930578063f6a1bb5014610907578063f9f031df146108c7578063faea02751461032c578063fb491ff5146103035763fc0c546a146102d857600080fd5b3461030057806003193601126103005760ca546040516001600160a01b039091168152602090f35b80fd5b503461030057806003193601126103005760cf546040516001600160a01b039091168152602090f35b5034610300576020366003190112610300576103466139bb565b61034e614630565b610356613e4c565b6001600160a01b03818116835260da6020526040832054166108b557604051630dfe168160e01b8152916020836004816001600160a01b0386165afa9283156108a8578193610887575b5060405163d21220a760e01b81526020816004816001600160a01b0387165afa90811561087c57829161085d575b5060ce5460405163d9a641e160e01b81526001600160a01b0386811660048301528381166024830152909160209183916044918391165afa90811561082157839161083e575b506001600160a01b0384811691160361082c57604051630a7d2f0d60e31b8152906020826004816001600160a01b0388165afa918215610821578392610800575b506001600160a01b038216156107ee576040516395d89b4160e01b8082529590919084836004816001600160a01b0385165afa9283156107c75785936107d2575b5060405196875284876004816001600160a01b0386165afa9283156107c75761054197869461079c575b50602161050e9160405195816104e088935180926020808701910161409c565b8201602f60f81b60208201526104ff825180936020878501910161409c565b01036001810186520184613a94565b60cc546001600160a01b031660206105258561457c565b604051809a8192632e5d509360e11b83528787600485016145c2565b038189855af1928315610791578680989961058897989561076d575b5061056a602095966145eb565b604051632e5d509360e11b81529788958694859391600485016145c2565b03925af191821561074157849261074c575b5060d05460ca5460c954604051633a6fa56960e11b81526001600160a01b03928316600482015290821660248201528782166044820152306064820152838216608482015284821660a4820152600160c482015294811660e486015260209185916101049183918991165af1928315610741578493610710575b5061062182828786614ce3565b60d65492600160401b8410156106fa57600184018060d6558410156106e45760d66000527fe767803f8ecf1dee6bb0345811f7312cda556058b19db6389ad9ae3568643ddd90930180546001600160a01b0319166001600160a01b039687161790556040519485946106e09491600191908316907f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa9080a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b0390f35b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b61073391935060203d60201161073a575b61072b8183613a94565b810190614299565b9138610614565b503d610721565b6040513d86823e3d90fd5b61076691925060203d60201161073a5761072b8183613a94565b903861059a565b6020955061078a61056a91873d891161073a5761072b8183613a94565b955061055d565b6040513d88823e3d90fd5b61050e9194506107bf6021913d808a833e6107b78183613a94565b81019061451e565b9491506104c0565b6040513d87823e3d90fd5b6107e79193503d8087833e6107b78183613a94565b9138610496565b60405163486aa30760e01b8152600490fd5b61081a91925060203d60201161073a5761072b8183613a94565b9038610455565b6040513d85823e3d90fd5b604051636f2631d760e11b8152600490fd5b610857915060203d60201161073a5761072b8183613a94565b38610414565b610876915060203d60201161073a5761072b8183613a94565b386103ce565b6040513d84823e3d90fd5b6108a191935060203d60201161073a5761072b8183613a94565b91386103a0565b50604051903d90823e3d90fd5b6040516307a59bfb60e01b8152600490fd5b5034610300576020366003190112610300576004356001600160401b038111610903576108fb610900913690600401613bca565b614752565b80f35b5080fd5b503461030057806003193601126103005760ce546040516001600160a01b039091168152602090f35b503461030057806003193601126103005760cc546040516001600160a01b039091168152602090f35b905034610903576020366003190112610903579061010091604061097b6139bb565b928083835161098981613a5d565b82815282602082015282858201528260608201528260808201528260a08201528260c0820152015260018060a01b03809416815260db60205220604051906109d082613a5d565b805460ff8116151594858452602084019160ff8160081c161515835281604086019160101c1681528180600186015416606087019081528180600288015416936080890194855260038801549660a08a019788528a600560048b01549a60c08d019b8c5201549a01998a526040519b8c5251151560208c0152511660408a015251166060880152511660808601525160a08501525160c08401525190820152f35b823461030057602036600319011261030057600435610a8e613c72565b8060d9557facb8ee69d640a5c0a33b625458f90a96b061a9583ea7dcd111eafacfec8c91c78280a280f35b82346103005760203660031901126103005760043560d6548110156109035760d66000527fe767803f8ecf1dee6bb0345811f7312cda556058b19db6389ad9ae3568643ddd01546040516001600160a01b039091168152602090f35b823461030057604036600319011261030057610900600435610b356139d1565b908084526065602052610b4e6001604086200154613f91565b6140e4565b8234610300576040366003190112610300576040610b6f6139d1565b91600435815260de602052209060018060a01b03166000526020526020604060002054604051908152f35b82346103005760c036600319011261030057610bb46139bb565b610bbc6139d1565b6001600160a01b0391604435908382168203610d8157606435928484168403610d81576001600160401b0393608435858111610d7d57610c00903690600401613ad0565b9460a435908111610d7d57610c19903690600401613ad0565b90610c22614630565b610c2a613e4c565b868316885260209460da86528760408a2054166108b5578760cc5416908960405198888a80610c69632e5d509360e11b948583528988600485016145c2565b038185885af1998a1561087c57829a610d54575b50908894939291610c9c604051978896879586948552600485016145c2565b03925af1938415610d4957948094939288926106e0977f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa9a96610d1d575b50506002929185610ceb9286614ce3565b60405197889784169180a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b610ceb929650600294939181610d3e92903d1061073a5761072b8183613a94565b959181939450610cda565b6040513d89823e3d90fd5b8995949392919a50610d7290863d881161073a5761072b8183613a94565b999091929394610c7d565b8780fd5b600080fd5b823461030057610d9536613b47565b9060ff60e15416610e6257610da981615724565b610db2816156f0565b610dba61568b565b610dc26156c1565b60d35483906001600160a01b0316803b1561090357818091604460405180948193631321852b60e01b83528860048401528960248401525af1801561087c57610e4a575b5050610e118261498b565b610e1a81615667565b610e2382615667565b7f8732e1d461da45518922d2df3664c17ef989dedb0b3ffa74b907d7b0f8babe1a8380a380f35b610e5390613a4a565b610e5e578284610e06565b8280fd5b604051631e653c7560e21b8152600490fd5b90503461090357602080600319360112610e5e576004356001600160401b038111610fce57610ea7903690600401613b17565b90845b828110610eb5578580f35b856001600160a01b0380610ed2610ecd85888861471a565b61472a565b16825260db86526040822086604051610eea81613a5d565b8960058454948660ff968781161515978887528160081c161515809787015260101c166040850152866001820154166060850152866002820154166080850152600381015460a0850152600481015460c0850152015491015281610fc6575b50610f5f575b5050610f5a9061470b565b610eaa565b604090610f70610ecd85888861471a565b16600482518094819363d294f09360e01b83525af18015610d4957610f97575b8690610f4f565b604090813d8311610fbf575b610fad8183613a94565b81010312610fbb5738610f90565b8580fd5b503d610fa3565b905038610f49565b8380fd5b8234610300576040366003190112610300576001600160401b03600435818111610e5e57611004903690600401613bca565b602435918211610e5e5761101f610900923690600401613c57565b90614817565b823461030057806003193601126103005760d2546040516001600160a01b039091168152602090f35b8234610300578060031936011261030057602060ff60e154166040519015158152f35b8234610300576020366003190112610300576004359060d45482101561030057602061109c836139fb565b905460405160039290921b1c6001600160a01b03168152f35b823461030057806003193601126103005760cd546040516001600160a01b039091168152602090f35b82346103005760203660031901126103005760209060ff906040906001600160a01b036111096139bb565b16815260db84522054166040519015158152f35b8234610300576020366003190112610300576020906001600160a01b0390604090826111476139bb565b16815260da8452205416604051908152f35b82346103005761116836613b47565b9190815260dc6020526040812090815483101561030057602061109c8484613a32565b8234610300578060031936011261030057602090604051908152f35b8234610300576020366003190112610300576111c16139bb565b6111c9613e4c565b6001600160a01b031680825260db602052604082205460081c60ff166112295780825260db60205260408220805461ff0019166101001790557fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa8280a280f35b604051634da5c68f60e11b8152600490fd5b905034610903576020366003190112610903576112566139bb565b61125e613e4c565b6001600160a01b0390811680845260db602052604080852090519193909161128581613a5d565b82549160ff8316151582528460ff8460081c1615938415602085015260101c166040830152846001850154166060830152846002850154166080830152600560038501549460a08401958652600481015460c085015201549101526113555782845260db60205260408420805461ff00191690555180611329575b50507f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba78280a280f35b8161133d9260ca54169060cb541690614318565b80825260db6020528160036040822001553880611300565b604051633f88da5160e21b8152600490fd5b82346103005760403660031901126103005760406113836139d1565b9160043581526065602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b823461030057602080600319360112610903576113ce6139bb565b906113d7614630565b6113df613e4c565b60018060a01b03918281169182855260da8152836040862054166108b5576024818560cd54166040519283809263e5e31b1360e01b82528860048301525afa908115610791578691611772575b501561082c57604051630dfe168160e01b8152918183600481875afa928315610791578693611753575b5060405163d21220a760e01b8152918083600481885afa928315610d49578793611734575b50604051630a7d2f0d60e31b81528181600481895afa80156116995787918991611717575b501680156107ee576040516395d89b4160e01b81529388856004818a5afa94851561170c5789956116f0575b508760cc54166114db8661457c565b968a604051978689806114fe632e5d509360e11b9d8e83528988600485016145c2565b038185885af198891561087c5782996116c3575b509061152187959493926145eb565b9961153a6040519b8c96879586948552600485016145c2565b03925af19485156116995788956116a4575b50818760cf54166101048960ca54168b8b60c95416936040519687958694633a6fa56960e11b8652600486015260248501528c60448501523060648501528d8b1660848501528d8c1660a4850152600060c485015260e48401525af191821561169957859185918a94611676575b505082936115c793614ce3565b60d55493600160401b8510156106fa57600185018060d5558510156106e4576106e09460d56000527f51858de9989bf7441865ebdadbf7382c8838edbf830f5d86a9a51ac773676dd601906001600160601b0360a01b82541617905560405195869582167f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa8280a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b6115c79450908161169292903d1061073a5761072b8183613a94565b928a6115ba565b6040513d8a823e3d90fd5b6116bc919550823d841161073a5761072b8183613a94565b938861154c565b87959493929199506116e461152191873d891161073a5761072b8183613a94565b99919293949550611512565b6117059195503d808b833e6107b78183613a94565b93896114cc565b6040513d8b823e3d90fd5b61172e9150833d851161073a5761072b8183613a94565b896114a0565b8161174c9294503d851161073a5761072b8183613a94565b918761147b565b61176b919350823d841161073a5761072b8183613a94565b9186611456565b6117929150823d8411611798575b61178a8183613a94565b810190614353565b8661142c565b503d611780565b8234610300576117ae36613b47565b90506117b8614630565b60ff60e15416610e625760c9546001600160a01b031633036117fe576117f6906117e061568b565b6117e86156c1565b6117f18161498b565b615667565b600160975580f35b604051634ca8886760e01b8152600490fd5b90503461090357602036600319011261090357610100916001600160a01b03906040908261183c6139bb565b16815260db6020522091825492826001820154168360028301541660038301549160056004850154940154956040519760ff81161515895260ff8160081c16151560208a015260101c1660408801526060870152608086015260a085015260c0840152820152f35b8234610300576060366003190112610300576004356001600160401b03602435818111610fce576118d9903690600401613b17565b9091604435908111611ab7576118f3903690600401613b17565b9290916118fe614630565b60ff60e15416610e625760c95460405163430c208160e01b815233600482015260248101879052602093916001600160a01b03919085908290604490829086165afa90811561170c578991611a9a575b50156117fe57858303611a885761196361568b565b60d3541691604051630794a69b60e21b81528760048201528481602481875afa90811561170c578991611a6b575b50611a5957836024936040519485809263d4e2616f60e01b82528b60048301525afa8015611699576119d2938991611a3c575b5015611a26575b3691613b74565b916119dc84613b5d565b936119ea6040519586613a94565b8085528285019060051b820191368311610d7d57905b828210611a1757876117f6886117f18989836151b7565b81358152908301908301611a00565b611a2e6156c1565b611a37876156f0565b6119cb565b611a539150853d87116117985761178a8183613a94565b896119c4565b604051633b524d7f60e11b8152600490fd5b611a829150853d87116117985761178a8183613a94565b89611991565b60405163512509d360e11b8152600490fd5b611ab19150853d87116117985761178a8183613a94565b8961194e565b8480fd5b8234610300578060031936011261030057606060d45460d55460d6549060405192835260208301526040820152f35b8234610300576060366003190112610300576001600160401b03600435818111610e5e57611b1c903690600401613bca565b906024908135908111610fce57611b37903690600401613c57565b60c95460405163430c208160e01b8152336004820152604480356024830181905290946001600160a01b039491939192909160209082908890829089165afa908115611699578891611c21575b50156117fe578693845b8751811015611c1d5781611ba2828a61473e565b5116611bae828661473e565b5190803b15610d7d57611be79188918a838a604051968795869485936353c2957d60e11b8552600485015260408d8501528301906147da565b03925af1908115610d49578791611c09575b5050611c049061470b565b611b8e565b611c1290613a4a565b610fbb57858a611bf9565b8580f35b611c39915060203d81116117985761178a8183613a94565b88611b84565b823461030057611c4e36613b47565b90611c57614630565b8260018060a01b03918260cb54166040519063ed29fc1160e01b82528160048160209687945af1908115610791578391611cd4575b50505b838110611c9f5784600160975580f35b8083611cad611ccf936139fb565b90549060031b1c16865260da8352611cca84604088205416614a4d565b61470b565b611c8f565b813d8311611cf6575b611ce78183613a94565b81010312610d81578186611c8c565b503d611cdd565b8234610300576020366003190112610300576040602091600435815260dc83522054604051908152f35b8234610300576040366003190112610300576040611d436139d1565b91600435815260df602052209060018060a01b03166000526020526020604060002054604051908152f35b8234610300576020366003190112610300576004356001600160401b03811161090357611d9f903690600401613b17565b611da7614630565b60cb5460405163ed29fc1160e01b8152906020908290600490829088906001600160a01b03165af1801561074157611e0c575b50825b818110611ded5783600160975580f35b80611cca611e02610ecd611e0794868861471a565b614a4d565b611ddd565b602090813d8111611e30575b611e228183613a94565b81010312610d815783611dda565b503d611e18565b82346103005780600319360112610300576020611e52614e7a565b604051908152f35b8234610300576020806003193601126109035760043590811515809203610e5e576000805260658152604060002033600052815260ff6040600020541615611ed2575060ff1960e1541660ff82161760e1557e3fd49cd010bbc93544a605b2f7e82759c9c4c8ab53202063ba094bc4094fe58280a280f35b611edb3361418b565b906000604051611eea81613a79565b604281528281019160603684378151156120775760308353815160019081101561206357607860218401536041905b808211612009575050611fc657611f9293611fa19260489260405196879376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b88860152611f69815180928a60378901910161409c565b8401917001034b99036b4b9b9b4b733903937b6329607d1b60378401525180938684019061409c565b01036028810185520183613a94565b611fc260405192839262461bcd60e51b8452600484015260248301906140bf565b0390fd5b6064836040519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f811660108110156106e4576f181899199a1a9b1b9c1cb0b131b232b360811b901a612038848661417a565b5360041c91801561204d576000190190611f19565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b81526032600452602490fd5b90503461090357602036600319011261090357602091604091600435825283522054604051908152f35b823461030057806003193601126103005760d3546040516001600160a01b039091168152602090f35b823461030057806003193601126103005760c9546040516001600160a01b039091168152602090f35b823461030057610160366003190112610300576004356001600160401b03811161090357612139903690600401613b17565b906001600160401b0360243511610e5e57604060243536036003190112610e5e576001600160401b0360443511610e5e57606060443536036003190112610e5e576001600160401b0360643511610e5e57608060643536036003190112610e5e576001600160401b0360843511610e5e5760a060843536036003190112610e5e5760c03660a3190112610e5e5760ca546001600160a01b03169060a435156129cf576040516370a0823160e01b8152336004820152602081602481865afa9081156107c757859161299e575b50925b80612988575b505060243560040161222081806148af565b905061295d575b5061223c6024604435016044356004016148af565b90506127f9575b612252600460643501806148af565b90506125c9575b60246084350135612502575b60a435612270578280f35b670de0b6b3a76400008060a435116124f0576040516370a0823160e01b81523360048201526020938482602481875afa9182156107915786926124bf575b50906122c06122c59260a43592614686565b61415a565b048015918215806122d8575b5050508280f35b60c9546001600160a01b0316936122f1843033866146b3565b90612452575b156123ee5760405163095ea7b360e01b858201526001600160a01b0384166024820152604481018390526123419161233c82606481015b03601f198101845283613a94565b61436b565b60e4356001600160a01b03811690819003610d81576101043592831515809403610d81576101243592831515809403610d81578660c492879560405197889687956342c15c8760e01b87526004870152863560248701526044860152606485015260848401526101443560a48401525af18015610821576123c5575b8080806122d1565b813d83116123e7575b6123d88183613a94565b81010312610d815781806123bd565b503d6123ce565b60405162461bcd60e51b815260048101859052603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b50604051636eb1769f60e11b81523060048201526001600160a01b03841660248201528481604481855afa908115610791578691612492575b50156122f7565b90508481813d83116124b8575b6124a98183613a94565b81010312610d8157518661248b565b503d61249f565b91508482813d83116124e9575b6124d68183613a94565b81010312610d81579051906122c06122ae565b503d6124cc565b604051638de4a0f760e01b8152600490fd5b60d2548390600460843501906001600160a01b03166125208261494c565b9061253e61253260446084350161494c565b936084803501906148af565b919093813b15610fbb5785809461259f604051978896879586946378c16d8760e01b8652336004870152151560248601526024608435013560448601521515606485015260646084350135608485015260c060a485015260c4840191614928565b03925af1801561087c576125b5575b5050612265565b6125be90613a4a565b610e5e5782846125ae565b9190815b6125dc600460643501806148af565b9050811015612661576125f4600460643501806148af565b33916001600160a01b039161260e91610ecd91869161471a565b160361261c576001016125cd565b60405162461bcd60e51b815260206004820152601e60248201527f757365727320636f6e7461696e6573206e6f206f6e6c792063616c6c657200006044820152606490fd5b5060d154919290916001600160a01b0316612681600460643501806148af565b6126986024606494939435016064356004016148af565b9390916126af6044606435016064356004016148af565b6126c260648035016064356004016148af565b949095873b156127f55761270f908c996126fd61272196959460049b9a9860806040519d8e6301c7ba5760e61b8152015260848d01916148e4565b8a81036003190160248c0152916148e4565b87810360031901604489015291614928565b91600319858403016064860152808352602083019060208160051b850101938388915b838310612782575050505050508391838381809403925af1801561087c5761276e575b5050612259565b61277790613a4a565b610e5e578284612767565b91939590929496979850601f198282030186528635601e19843603018112156127f157830190602082359201916001600160401b0381116127ed578060051b360383136127ed576127d96020928392600195614928565b98019601930190918b989796959492612744565b8d80fd5b8c80fd5b8b80fd5b9061283c6128116024604435016044356004016148af565b61283461282a60448095949535016044356004016148af565b9490923691613b74565b923691613be8565b60c95460405163430c208160e01b815233600480830191909152604480359091013560248301529293929091602091839182906001600160a01b03165afa90811561079157869161293f575b50156117fe578492835b8251811015612934576001600160a01b036128ad828561473e565b51166128b9828661473e565b5190803b15612930576128fa87929183926040519485809481936353c2957d60e11b83526044356004013560048401526040602484015260448301906147da565b03925af190811561079157869161291c575b50506129179061470b565b612892565b61292590613a4a565b611ab757848861290c565b8680fd5b509392505050612243565b612957915060203d81116117985761178a8183613a94565b86612888565b8061101f61282a61283461297484612982966148af565b9290946024803501906148af565b83612227565b612997916108fb913691613b74565b838061220e565b90506020813d82116129c7575b816129b860209383613a94565b81010312610d81575185612205565b3d91506129ab565b8392612208565b8234610300576040366003190112610300576129f06139bb565b6129f86139d1565b9082549160ff8360081c161591828093612cee575b8015612cd7575b15612c7b5760ff1984811660011786559383612c6a575b506001600160a01b0391818316918215612c585786926002604360981b0191823b15611ab75784809360646040518098819363c8992e6160e01b835260026004840152600160248401528760448401525af1948515610821578695612c3f575b503b612bf0575b5060ff612aba915460081c16612aa7816142b8565b612ab0816142b8565b60016097556142b8565b3360009081527fffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b602090815260409091205490959060ff1615612ba1575b5016906004846001600160601b0360a01b93808560c954161760c95560405192838092637e062a3560e11b82525afa908115610791578691612b84575b50169060ca54161760ca55610e1060d955612b4e575080f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989061ff0019835416835560405160018152a180f35b612b9b9150853d871161073a5761072b8183613a94565b86612b35565b600080526065865260406000203360005286526001604060002091825416179055333360007f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a486612af8565b809193503b15610e5e578280916024604051809681936305573b1760e31b83523060048401525af19283156108a857849315612a9257612c3291929350613a4a565b611ab75781908587612a92565b83919550612c4c90613a4a565b61090357849389612a8b565b604051639fabe1c160e01b8152600490fd5b61ffff191661010117855585612a2b565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b158015612a145750600160ff851614612a14565b50600160ff851610612a0d565b8234610300578060031936011261030057612d14614630565b60cb5460405163ed29fc1160e01b81526020916001600160a01b039190839082906004908290889087165af1908115610741578391612d79575b505060d45491835b838110612d665784600160975580f35b8083611cad612d74936139fb565b612d56565b813d8311612d9b575b612d8c8183613a94565b81010312610d81578184612d4e565b503d612d82565b8234610300576040366003190112610300576004356001600160401b03811161090357612dd3903690600401613ad0565b612ddb6139d1565b612de3613c72565b6040516020810190612e0d6020828651612e008187858b0161409c565b8101038084520182613a94565b5190207f39eb9ec2059d897c44a17440c762c429de204f6fddd727156ca52b8da086a6f78103612e8b575060cb80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e85905b6040519182916020835260018060a01b03169460208301906140bf565b0390a280f35b7ff23a19003b02ccc6ddd73a13c071e09977c34bfd7b5318a44fe456d9a77dd0af8103612ee7575060cc80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f18c95c463f9590b3f298aef56c7cfb639672452cd99ac8d92a9fc0e2ef46ab558103612f43575060d180546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fbbbfaae454470f56db24caaffaae3a4d3d0ed7a761421871150faa442416ea838103612f9f575060d280546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f8ba8cbf9a47db7b5e8ae6c0bff072ed6faefec4a0722891b09f22b7ac343fd4f8103612ffb575060d380546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fa0238e972eab1b5ee9c4988c955a7165a662b3206031ac6ac27a3066d669a28d8103613057575060cd80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fb8e13a5900588d0607f820e1a839eb41b418c77b9db23e333bcc679d611dbc9b81036130b3575060ce80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fe8ee2fdef59c2203ee9a363d82083446f25f27a1aff8fc1f0f3f79b83d30305c810361310f575060cf80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f7ebf69e1e15f4a4db2cb161251ab5c47f9f68d65713eba9542fedffbe59b7931036131695760d080546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b604051634ba4f8e160e01b8152600490fd5b823461030057806003193601126103005760d0546040516001600160a01b039091168152602090f35b9050346109035760203660031901126109035760cb54600435916001600160a01b0391821633036117fe576131e1838360ca5416309033906146b3565b6131e9614e7a565b62093a7f19810190811161327357845260205260408320548061323c575b5060ca5416906040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a380f35b670de0b6b3a76400008084029084820414841517156132735761326a9161326291614693565b60d75461416d565b60d75538613207565b634e487b7160e01b85526011600452602485fd5b82346103005760203660031901126103005760043560d5548110156109035760d56000527f51858de9989bf7441865ebdadbf7382c8838edbf830f5d86a9a51ac773676dd601546040516001600160a01b039091168152602090f35b8234610300576040366003190112610300576132fd6139d1565b336001600160a01b0382160361331957610900906004356140e4565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b82346103005760208060031936011261090357600435613394614630565b60ff60e15416610e625760c95460405163430c208160e01b8152336004820152602481018390526001600160a01b039184908290604490829086165afa9081156107c75785916134a5575b50156117fe576133ed61568b565b60d35416604051630794a69b60e21b81528260048201528381602481855afa9081156107c7578591613488575b50611a5957826024916040519283809263d4e2616f60e01b82528660048301525afa908115610741576117f693859261346b575b50501561345e576117f18161498b565b6134666156c1565b6117e8565b6134819250803d106117985761178a8183613a94565b848061344e565b61349f9150843d86116117985761178a8183613a94565b8561341a565b6134bc9150843d86116117985761178a8183613a94565b856133df565b8234610300576020366003190112610300576004356134df614630565b60ff60e15416610e6257806134f48392615724565b6134fd816156f0565b61350561568b565b61350d6156c1565b61351681614eec565b61351f81615667565b60c9546001600160a01b031690813b1561357f57829160448392604051948593849262bcea6b60e31b845260048401528160248401525af1801561087c5761356b575b50600160975580f35b61357490613a4a565b610300578082613562565b5050fd5b823461030057806003193601126103005760d1546040516001600160a01b039091168152602090f35b8234610300576040366003190112610300576004356135c96139d1565b81835260656020526135e16001604085200154613f91565b81600052606560205260406000209060018060a01b0316908160005260205260ff6040600020541615613612578280f35b8160005260656020526040600020816000526020526040600020600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a481808280f35b8234610300578060031936011261030057602060d754604051908152f35b8234610300578060031936011261030057602060d854604051908152f35b82346103005760203660031901126103005760016040602092600435815260658452200154604051908152f35b8234610300578060031936011261030057602060d954604051908152f35b82346103005760203660031901126103005760209060ff906040906001600160a01b036137166139bb565b16815260db8452205460081c166040519015158152f35b823461030057602090816003193601126103005760043560ff60e15416610e625761375781615724565b613760816156f0565b61376861568b565b6137706156c1565b60d354604051631e60c38b60e01b8152600481018390526001600160a01b0394909185168183602481845afa9283156107c75785936138a4575b50803b15611ab7578480916024604051809481936302aba62f60e11b83528960048401525af180156107c75761388e575b5080602493949560c95416604051948580926303d35ae760e51b82528660048301525afa9081156107c757859161385e575b5061382992506138505761382081614eec565b6117f183615667565b7febf777a6065df32120bd289932378b01179d9a5df170d4a43e8ca97e16e17ea08280a280f35b6138598161498b565b613820565b905082813d8311613887575b6138748183613a94565b81010312610d815761382991518561380d565b503d61386a565b6024939461389c8392613a4a565b9493506137db565b9092508181813d83116138cc575b6138bc8183613a94565b81010312611ab7575191866137aa565b503d6138b2565b8234610300576020366003190112610300576040602091600435815260dd83522054604051908152f35b823461030057806003193601126103005760cb546040516001600160a01b039091168152602090f35b8234610300576020366003190112610300576020906001600160a01b0390600290604090836139536139bb565b16815260db855220015416604051908152f35b8390346109035760203660031901126109035760043563ffffffff60e01b8116809103610e5e5760209250637965db0b60e01b81149081156139aa575b5015158152f35b6301ffc9a760e01b149050836139a3565b600435906001600160a01b0382168203610d8157565b602435906001600160a01b0382168203610d8157565b35906001600160a01b0382168203610d8157565b60d4548110156106e45760d46000527f9780e26d96b1f2a9a18ef8fc72d589dbf03ef788137b64f43897e83a91e7feec0190600090565b80548210156106e45760005260206000200190600090565b6001600160401b0381116106fa57604052565b61010081019081106001600160401b038211176106fa57604052565b608081019081106001600160401b038211176106fa57604052565b90601f801991011681019081106001600160401b038211176106fa57604052565b6001600160401b0381116106fa57601f01601f191660200190565b81601f82011215610d8157803590613ae782613ab5565b92613af56040519485613a94565b82845260208383010111610d8157816000926020809301838601378301015290565b9181601f84011215610d81578235916001600160401b038311610d81576020808501948460051b010111610d8157565b6040906003190112610d81576004359060243590565b6001600160401b0381116106fa5760051b60200190565b9291613b7f82613b5d565b91613b8d6040519384613a94565b829481845260208094019160051b8101928311610d8157905b828210613bb35750505050565b838091613bbf846139e7565b815201910190613ba6565b9080601f83011215610d8157816020613be593359101613b74565b90565b92919092613bf584613b5d565b91613c036040519384613a94565b829480845260208094019060051b830192828411610d815780915b848310613c2d57505050505050565b82356001600160401b038111610d81578691613c4c8684938601613bca565b815201920191613c1e565b9080601f83011215610d8157816020613be593359101613be8565b3360009081527f847e30cfc3a73cca7644072137344a252d944ea904fdca79afe80a7c92f5b306602090815260408083205490927fdb77e4a0d65a915fbfe630f3ac05f3a2259b73e7baead93657155a1e625c8b5e9160ff1615613cd65750505050565b613cdf3361418b565b91845190613cec82613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b818311613dde57505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b01036028810187520185613a94565b5192839262461bcd60e51b8452600484015260248301906140bf565b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a613e0e858761417a565b5360041c928015613e2457600019019190613d1d565b634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526032600452602483fd5b3360009081527f2ef62106252d7662ea25efd6fda847ba96543a6fb24982c308bdc4485c3fb5fb602090815260408083205490927f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19160ff1615613eb05750505050565b613eb93361418b565b91845190613ec682613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b818311613f4b57505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a613f7b858761417a565b5360041c928015613e2457600019019190613ef7565b60009080825260209060658252604092838120338252835260ff848220541615613fbb5750505050565b613fc43361418b565b91845190613fd182613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b81831161405657505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a614086858761417a565b5360041c928015613e2457600019019190614002565b60005b8381106140af5750506000910152565b818101518382015260200161409f565b906020916140d88151809281855285808601910161409c565b601f01601f1916010190565b906000918083526065602052604083209160018060a01b03169182845260205260ff60408420541661411557505050565b8083526065602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b8181029291811591840414171561204d57565b9190820180921161204d57565b9081518110156106e4570160200190565b60405190606082018281106001600160401b038211176106fa57604052602a82526020820160403682378251156106e4576030905381516001908110156106e457607860218401536029905b80821161422b5750506141e75790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f81166010811015614284576f181899199a1a9b1b9c1cb0b131b232b360811b901a61425a848661417a565b5360041c91801561426f5760001901906141d7565b60246000634e487b7160e01b81526011600452fd5b60246000634e487b7160e01b81526032600452fd5b90816020910312610d8157516001600160a01b0381168103610d815790565b156142bf57565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448201929092526143519161233c826064810161232e565b565b90816020910312610d8157518015158103610d815790565b60408051908101916001600160a01b03166001600160401b038311828410176106fa576143f9926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d15614481573d916143dd83613ab5565b926143eb6040519485613a94565b83523d60008785013e614485565b805190828215928315614469575b505050156144125750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6144799350820181019101614353565b388281614407565b6060915b919290156144e75750815115614499575090565b3b156144a25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156144fa5750805190602001fd5b60405162461bcd60e51b815260206004820152908190611fc29060248301906140bf565b602081830312610d81578051906001600160401b038211610d81570181601f82011215610d8157805161455081613ab5565b9261455e6040519485613a94565b81845260208284010111610d8157613be5916020808501910161409c565b90614351602f60405180946e02332b734bc102628102332b2b99d1608d1b60208301526145b2815180926020868601910161409c565b810103600f810185520183613a94565b6001600160a01b03918216815291166020820152606060408201819052613be5929101906140bf565b90614351602e60405180946d02332b734bc10213934b132b99d160951b6020830152614620815180926020868601910161409c565b810103600e810185520183613a94565b600260975414614641576002609755565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b9190820391821161204d57565b811561469d570490565b634e487b7160e01b600052601260045260246000fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b038411176106fa576143519260405261436b565b600019811461204d5760010190565b91908110156106e45760051b0190565b356001600160a01b0381168103610d815790565b80518210156106e45760209160051b010190565b906000915b80518310156147d5576001600160a01b03614772848361473e565b511692833b15610d81576000806040956024875180948193630c00007b60e41b83523360048401525af180156147ca576147b4939495506147bb575b5061470b565b9190614757565b6147c490613a4a565b386147ae565b85513d6000823e3d90fd5b509050565b90815180825260208080930193019160005b8281106147fa575050505090565b83516001600160a01b0316855293810193928101926001016147ec565b91909160005b81518110156148a9576001600160a01b03614838828461473e565b5116614844828661473e565b51813b15610d8157600061487e819360409384519586809481936336c3aa6160e21b835233600484015288602484015260448301906147da565b03925af190811561489f57509061489a92916147bb575061470b565b61481d565b513d6000823e3d90fd5b50509050565b903590601e1981360301821215610d8157018035906001600160401b038211610d8157602001918160051b36038313610d8157565b91908082526020809201929160005b828110614901575050505090565b909192938280600192838060a01b03614919896139e7565b168152019501939291016148f3565b81835290916001600160fb1b038311610d815760209260051b809284830137010190565b358015158103610d815790565b9061496382613b5d565b6149706040519182613a94565b8281528092614981601f1991613b5d565b0190602036910137565b9060009180835260209260dc8452604093848220908551808383829554938481520190865283862092865b85828210614a2e575050506149cd92500383613a94565b6149d78251614959565b93835b8351811015614a1e5781855260de8352878520600191906001600160a01b03614a03838861473e565b51168752845288862054614a17828961473e565b52016149da565b50919250506143519394506151b7565b85546001600160a01b03168452600195860195889550930192016149b6565b9060018060a01b03918281169060009382855260209060db8252604090818720938251614a7981613a5d565b85549560ff871615158252838683019760ff8160081c161515895260101c16858301528360018201541660608301528360028201541660808301908152600382015460a084015260e0600560048401549360c0860194855201549301928352614ae0614e7a565b80935110614af6575b5050505050505050509050565b62093a7f198301838111614ccf578b5260df875284868c209151168b528652848a20549081614c60575b505060d754878a5260db8652848a20906004820155600381018054978815159081614c55575b5015614ae9578a90556005015560ca54835163095ea7b360e01b81526001600160a01b03929092166004830152602482018690528490829060449082908c9087165af18015614c4b57614c2e575b5060ca541686853b1561030057825163b66503cf60e01b81526001600160a01b03929092166004830152602482018590528160448183895af18015614c24577f4fa9693cae526341d334e2862ca2413b2e503f1266255f9e0869fb36e6d89b179495969750614c15575b50519283523392a380388080808080808080614ae9565b614c1e90613a4a565b38614bfe565b82513d89823e3d90fd5b614c4490843d86116117985761178a8183613a94565b5038614b94565b83513d8a823e3d90fd5b905051151538614b46565b60d7549051614c6e91614686565b908115614b2057670de0b6b3a764000091614c889161415a565b0486511515600014614cb657878a5260db8652614cac6003868c200191825461416d565b90555b3880614b20565b614cca908460ca54168560cb541690614318565b614caf565b634e487b7160e01b8c52601160045260248cfd5b9192909260d7549260405192614cf884613a5d565b600184526020840190600182526040850194600560018060a01b03948587168852856060840194169889855286608085019b169a8b815260a085019060009788835260c087019384528960e08801958a8752169b8c8a5260db60205260408a20975115159160ff61ff008a549251151560081b169262010000600160b01b03905160101b1693169069ffffffffffffffffffff60b01b1617171786558860018701975116966001600160601b0360a01b97888254161790558860028701915116878254161790555160038501555160048401555191015586825260da60205284604083209182541617905560d45490600160401b821015614e66575091614e6191614e2a8460017fa4d97e9e7c65249b4cd01acb82add613adea98af32daf092366982f0a0d4e453960160d4556139fb565b815460039190911b92831b19169188901b919091179055604080513381526001600160a01b03909216602083015290918291820190565b0390a4565b634e487b7160e01b81526041600452602490fd5b60cb54604051631a2732c160e31b815290602090829060049082906001600160a01b03165afa908115614ee057600091614eb2575090565b906020823d8211614ed8575b81614ecb60209383613a94565b8101031261030057505190565b3d9150614ebe565b6040513d6000823e3d90fd5b908160005260dc60205260406000206040518082602082945493848152019060005260206000209260005b818110615195575050614f2c92500382613a94565b600090614f37614e7a565b8460005260dd6020526040600020549160005b81518110156150fb576001600160a01b03614f65828461473e565b51168760005260de90816020526040600020816000526020526040600020549182614f9c575b505050614f979061470b565b614f4a565b896000526020526040600020816000526020526000604081205584861015614fc5575b80614f8b565b8460005260df6020526040600020816000526020526040600020614fea838254614686565b905560005260da60205260018060a01b03604060002054168060005260db60205260018060a01b0360406000205460101c16803b15610d81576000809160448c6040519485938492630441a3e760e41b845289600485015260248401525af18015614ee0576150ec575b50600081815260db60205260409020600101546001600160a01b0316803b15610d81576000809160448c6040519485938492630441a3e760e41b845289600485015260248401525af18015614ee0576150dd575b5060005260db60205260ff60406000205460081c166150c8575b80614fbf565b6150d690614f97929661416d565b94906150c2565b6150e690613a4a565b386150a8565b6150f590613a4a565b38615054565b505080939492911015615175575b8160005260dc60205260406000208054906000815581615154575b50506040519081527fad26e79d8a0938a06f8edf8d669ea711acf484fee628338db569984c46299aad60203392a4565b6000526020600020908101905b818110156151245760008155600101615161565b8260005260e0602052604060002061518e828254614686565b9055615109565b84546001600160a01b0316835260019485019486945060209093019201614f17565b90602492916151c581614eec565b60c9546040516339f890b560e21b8152600481018390529460209186919082906001600160a01b03165afa938415614ee057600094615633575b506000948594865b84518810156152e1576001600160a01b03615222898761473e565b511660005260da60205260018060a01b036040600020541660005260db602052604060002060405161525381613a5d565b815460ff80821615158352600882901c16158015602084015260109190911c6001600160a01b0390811660408401526001840154811660608401526002840154166080830152600383015460a0830152600483015460c083015260059092015460e090910152611355576152d56152db916152ce8a8961473e565b519061416d565b9761470b565b96615207565b90939194929596506152f1614e7a565b906152fc8351614959565b946000985b84518a101561551d57859697989960018060a01b03615320828861473e565b51166001600160a01b03615334838961473e565b511660005260da602052615369856153648661535e8660018060a01b03604060002054169d61473e565b5161415a565b614693565b92831561550b578c60005260de6020526040600020826000526020526040600020546154f9578c60005260dc602052604060002090815492600160401b8410156106fa578e6153c2858895600161542298018155613a32565b81549060031b9084821b9160018060a01b03901b191617905560005260de602052604060002081600052602052826040600020558860005260df602052604060002090600052602052604060002061541b83825461416d565b905561416d565b968261542e838c61473e565b52600081815260db602052604090205460101c6001600160a01b0316803b15610d81576000809160448f6040519485938492631c57762b60e31b84528a600485015260248401525af18015614ee0576154ea575b50600090815260db60205260409020600101546001600160a01b031691823b15610d81578b6044600092836040519687948593631c57762b60e31b8552600485015260248401525af1918215614ee0576154e0926147bb575061470b565b9897969594615301565b6154f390613a4a565b38615482565b6040516308595bd160e41b8152600490fd5b604051630dc5e4d360e01b8152600490fd5b939450505094939550801515806155db575b8560005260e0602052604060002061554883825461416d565b9055615556575b5050505050565b61556b604051926060845260608401906147da565b82810360208401526020808551928381520194019060005b8181106155c557505050907f8eb57ab77951661aa38ed1c05093da478521c8ea49e1a28b88cac6f98b79cf669160408201528033930390a4388080808061554f565b8251865260209586019590920191600101615583565b60c9546001600160a01b0316803b15610d81576000809160446040518094819362bcea6b60e31b83528b6004840152600160248401525af18015614ee057615624575b5061552f565b61562d90613a4a565b3861561e565b90936020823d60201161565f575b8161564e60209383613a94565b8101031261030057505192386151ff565b3d9150615641565b61566f614e7a565b906001820180921161204d5760005260dd602052604060002055565b6156a661569d62093a80420642614686565b60d9549061416d565b4211156156af57565b6040516329f7110f60e21b8152600490fd5b62093a806156d181420642614686565b90810180911161204d5760d9546156e791614686565b4210156156af57565b60005260dd60205261570a60406000205460d8549061416d565b421061571257565b60405163e119bce960e01b8152600490fd5b60c9546001600160a01b03163381900361573c575050565b60405163430c208160e01b81523360048201526024810192909252602090829060449082905afa908115614ee05760009161577a575b50156117fe57565b615792915060203d81116117985761178a8183613a94565b3861577256feb0377b4035c2bd5b6aeb3eb7f139162c730ea47b957e4354f8a81ebb99573deca164736f6c6343000813000a00000000000000000000000072e47b1eaaaac6c07ea4071f1d0d355f603e1cc1
Deployed Bytecode
0x608080604052600436101561001357600080fd5b60009060e08235811c91826301ffc9a7146139665750816306d6a1b21461392657816307546172146138fd57816309ccce12146138d357816312dd72001461372d5781631703e5f9146136eb5781631e853c26146136cd578163248a9ca3146136a0578163292fc979146136825781632986c0e5146136645781632f2ff15d146135ac5781632fa4abea14613583578163310bd74b146134c257816332145f901461337657816336568abe146132e3578163396d79d0146132875781633c6b16ab146131a45781633d8534421461317b5781633e21750a14612da2578163436596c414612cfb578163485cc955146129d65781634bf002ab146121075781634f2bfe5b146120de578163537f95ff146120b55781635534d8f91461208b57816355e5a30814611e5a5781636103ed2e14611e375781636138889b14611d6e57816364279c4b14611d275781636447e7da14611cfd5781637625391a14611c3f5781637715ee7514611aea57816377f11e4614611abb5781637ac09bf7146118a45781637d55c17a146118105781637f246f671461179f5781638ec6383e146113b357816391d1485414611367578163992a79331461123b5781639f06247b146111a7578163a217fddf1461118b578163a86a366d14611159578163aa3f22b81461111d578163aa79979b146110de578163ab138846146110b5578163ac4afa3814611071578163ae66c9ea1461104e578163af3b629a14611025578163c2b79e9814610fd2578163c527ee1f14610e74578163ca82240d14610d86578163cea033d014610b9a578163d23254b414610b53578163d547741f14610b15578163d9f3416f14610ab9578163df0915ea14610a71578163e839663b1461095957508063eb4a78e014610930578063f6a1bb5014610907578063f9f031df146108c7578063faea02751461032c578063fb491ff5146103035763fc0c546a146102d857600080fd5b3461030057806003193601126103005760ca546040516001600160a01b039091168152602090f35b80fd5b503461030057806003193601126103005760cf546040516001600160a01b039091168152602090f35b5034610300576020366003190112610300576103466139bb565b61034e614630565b610356613e4c565b6001600160a01b03818116835260da6020526040832054166108b557604051630dfe168160e01b8152916020836004816001600160a01b0386165afa9283156108a8578193610887575b5060405163d21220a760e01b81526020816004816001600160a01b0387165afa90811561087c57829161085d575b5060ce5460405163d9a641e160e01b81526001600160a01b0386811660048301528381166024830152909160209183916044918391165afa90811561082157839161083e575b506001600160a01b0384811691160361082c57604051630a7d2f0d60e31b8152906020826004816001600160a01b0388165afa918215610821578392610800575b506001600160a01b038216156107ee576040516395d89b4160e01b8082529590919084836004816001600160a01b0385165afa9283156107c75785936107d2575b5060405196875284876004816001600160a01b0386165afa9283156107c75761054197869461079c575b50602161050e9160405195816104e088935180926020808701910161409c565b8201602f60f81b60208201526104ff825180936020878501910161409c565b01036001810186520184613a94565b60cc546001600160a01b031660206105258561457c565b604051809a8192632e5d509360e11b83528787600485016145c2565b038189855af1928315610791578680989961058897989561076d575b5061056a602095966145eb565b604051632e5d509360e11b81529788958694859391600485016145c2565b03925af191821561074157849261074c575b5060d05460ca5460c954604051633a6fa56960e11b81526001600160a01b03928316600482015290821660248201528782166044820152306064820152838216608482015284821660a4820152600160c482015294811660e486015260209185916101049183918991165af1928315610741578493610710575b5061062182828786614ce3565b60d65492600160401b8410156106fa57600184018060d6558410156106e45760d66000527fe767803f8ecf1dee6bb0345811f7312cda556058b19db6389ad9ae3568643ddd90930180546001600160a01b0319166001600160a01b039687161790556040519485946106e09491600191908316907f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa9080a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b0390f35b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b61073391935060203d60201161073a575b61072b8183613a94565b810190614299565b9138610614565b503d610721565b6040513d86823e3d90fd5b61076691925060203d60201161073a5761072b8183613a94565b903861059a565b6020955061078a61056a91873d891161073a5761072b8183613a94565b955061055d565b6040513d88823e3d90fd5b61050e9194506107bf6021913d808a833e6107b78183613a94565b81019061451e565b9491506104c0565b6040513d87823e3d90fd5b6107e79193503d8087833e6107b78183613a94565b9138610496565b60405163486aa30760e01b8152600490fd5b61081a91925060203d60201161073a5761072b8183613a94565b9038610455565b6040513d85823e3d90fd5b604051636f2631d760e11b8152600490fd5b610857915060203d60201161073a5761072b8183613a94565b38610414565b610876915060203d60201161073a5761072b8183613a94565b386103ce565b6040513d84823e3d90fd5b6108a191935060203d60201161073a5761072b8183613a94565b91386103a0565b50604051903d90823e3d90fd5b6040516307a59bfb60e01b8152600490fd5b5034610300576020366003190112610300576004356001600160401b038111610903576108fb610900913690600401613bca565b614752565b80f35b5080fd5b503461030057806003193601126103005760ce546040516001600160a01b039091168152602090f35b503461030057806003193601126103005760cc546040516001600160a01b039091168152602090f35b905034610903576020366003190112610903579061010091604061097b6139bb565b928083835161098981613a5d565b82815282602082015282858201528260608201528260808201528260a08201528260c0820152015260018060a01b03809416815260db60205220604051906109d082613a5d565b805460ff8116151594858452602084019160ff8160081c161515835281604086019160101c1681528180600186015416606087019081528180600288015416936080890194855260038801549660a08a019788528a600560048b01549a60c08d019b8c5201549a01998a526040519b8c5251151560208c0152511660408a015251166060880152511660808601525160a08501525160c08401525190820152f35b823461030057602036600319011261030057600435610a8e613c72565b8060d9557facb8ee69d640a5c0a33b625458f90a96b061a9583ea7dcd111eafacfec8c91c78280a280f35b82346103005760203660031901126103005760043560d6548110156109035760d66000527fe767803f8ecf1dee6bb0345811f7312cda556058b19db6389ad9ae3568643ddd01546040516001600160a01b039091168152602090f35b823461030057604036600319011261030057610900600435610b356139d1565b908084526065602052610b4e6001604086200154613f91565b6140e4565b8234610300576040366003190112610300576040610b6f6139d1565b91600435815260de602052209060018060a01b03166000526020526020604060002054604051908152f35b82346103005760c036600319011261030057610bb46139bb565b610bbc6139d1565b6001600160a01b0391604435908382168203610d8157606435928484168403610d81576001600160401b0393608435858111610d7d57610c00903690600401613ad0565b9460a435908111610d7d57610c19903690600401613ad0565b90610c22614630565b610c2a613e4c565b868316885260209460da86528760408a2054166108b5578760cc5416908960405198888a80610c69632e5d509360e11b948583528988600485016145c2565b038185885af1998a1561087c57829a610d54575b50908894939291610c9c604051978896879586948552600485016145c2565b03925af1938415610d4957948094939288926106e0977f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa9a96610d1d575b50506002929185610ceb9286614ce3565b60405197889784169180a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b610ceb929650600294939181610d3e92903d1061073a5761072b8183613a94565b959181939450610cda565b6040513d89823e3d90fd5b8995949392919a50610d7290863d881161073a5761072b8183613a94565b999091929394610c7d565b8780fd5b600080fd5b823461030057610d9536613b47565b9060ff60e15416610e6257610da981615724565b610db2816156f0565b610dba61568b565b610dc26156c1565b60d35483906001600160a01b0316803b1561090357818091604460405180948193631321852b60e01b83528860048401528960248401525af1801561087c57610e4a575b5050610e118261498b565b610e1a81615667565b610e2382615667565b7f8732e1d461da45518922d2df3664c17ef989dedb0b3ffa74b907d7b0f8babe1a8380a380f35b610e5390613a4a565b610e5e578284610e06565b8280fd5b604051631e653c7560e21b8152600490fd5b90503461090357602080600319360112610e5e576004356001600160401b038111610fce57610ea7903690600401613b17565b90845b828110610eb5578580f35b856001600160a01b0380610ed2610ecd85888861471a565b61472a565b16825260db86526040822086604051610eea81613a5d565b8960058454948660ff968781161515978887528160081c161515809787015260101c166040850152866001820154166060850152866002820154166080850152600381015460a0850152600481015460c0850152015491015281610fc6575b50610f5f575b5050610f5a9061470b565b610eaa565b604090610f70610ecd85888861471a565b16600482518094819363d294f09360e01b83525af18015610d4957610f97575b8690610f4f565b604090813d8311610fbf575b610fad8183613a94565b81010312610fbb5738610f90565b8580fd5b503d610fa3565b905038610f49565b8380fd5b8234610300576040366003190112610300576001600160401b03600435818111610e5e57611004903690600401613bca565b602435918211610e5e5761101f610900923690600401613c57565b90614817565b823461030057806003193601126103005760d2546040516001600160a01b039091168152602090f35b8234610300578060031936011261030057602060ff60e154166040519015158152f35b8234610300576020366003190112610300576004359060d45482101561030057602061109c836139fb565b905460405160039290921b1c6001600160a01b03168152f35b823461030057806003193601126103005760cd546040516001600160a01b039091168152602090f35b82346103005760203660031901126103005760209060ff906040906001600160a01b036111096139bb565b16815260db84522054166040519015158152f35b8234610300576020366003190112610300576020906001600160a01b0390604090826111476139bb565b16815260da8452205416604051908152f35b82346103005761116836613b47565b9190815260dc6020526040812090815483101561030057602061109c8484613a32565b8234610300578060031936011261030057602090604051908152f35b8234610300576020366003190112610300576111c16139bb565b6111c9613e4c565b6001600160a01b031680825260db602052604082205460081c60ff166112295780825260db60205260408220805461ff0019166101001790557fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa8280a280f35b604051634da5c68f60e11b8152600490fd5b905034610903576020366003190112610903576112566139bb565b61125e613e4c565b6001600160a01b0390811680845260db602052604080852090519193909161128581613a5d565b82549160ff8316151582528460ff8460081c1615938415602085015260101c166040830152846001850154166060830152846002850154166080830152600560038501549460a08401958652600481015460c085015201549101526113555782845260db60205260408420805461ff00191690555180611329575b50507f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba78280a280f35b8161133d9260ca54169060cb541690614318565b80825260db6020528160036040822001553880611300565b604051633f88da5160e21b8152600490fd5b82346103005760403660031901126103005760406113836139d1565b9160043581526065602052209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b823461030057602080600319360112610903576113ce6139bb565b906113d7614630565b6113df613e4c565b60018060a01b03918281169182855260da8152836040862054166108b5576024818560cd54166040519283809263e5e31b1360e01b82528860048301525afa908115610791578691611772575b501561082c57604051630dfe168160e01b8152918183600481875afa928315610791578693611753575b5060405163d21220a760e01b8152918083600481885afa928315610d49578793611734575b50604051630a7d2f0d60e31b81528181600481895afa80156116995787918991611717575b501680156107ee576040516395d89b4160e01b81529388856004818a5afa94851561170c5789956116f0575b508760cc54166114db8661457c565b968a604051978689806114fe632e5d509360e11b9d8e83528988600485016145c2565b038185885af198891561087c5782996116c3575b509061152187959493926145eb565b9961153a6040519b8c96879586948552600485016145c2565b03925af19485156116995788956116a4575b50818760cf54166101048960ca54168b8b60c95416936040519687958694633a6fa56960e11b8652600486015260248501528c60448501523060648501528d8b1660848501528d8c1660a4850152600060c485015260e48401525af191821561169957859185918a94611676575b505082936115c793614ce3565b60d55493600160401b8510156106fa57600185018060d5558510156106e4576106e09460d56000527f51858de9989bf7441865ebdadbf7382c8838edbf830f5d86a9a51ac773676dd601906001600160601b0360a01b82541617905560405195869582167f8089d6e82213fccecc76e07d1324847c14fc6983332bc75f21a5a2dd74c07eaa8280a360016097556001600160a01b03908116855290811660208501521660408301526060820190565b6115c79450908161169292903d1061073a5761072b8183613a94565b928a6115ba565b6040513d8a823e3d90fd5b6116bc919550823d841161073a5761072b8183613a94565b938861154c565b87959493929199506116e461152191873d891161073a5761072b8183613a94565b99919293949550611512565b6117059195503d808b833e6107b78183613a94565b93896114cc565b6040513d8b823e3d90fd5b61172e9150833d851161073a5761072b8183613a94565b896114a0565b8161174c9294503d851161073a5761072b8183613a94565b918761147b565b61176b919350823d841161073a5761072b8183613a94565b9186611456565b6117929150823d8411611798575b61178a8183613a94565b810190614353565b8661142c565b503d611780565b8234610300576117ae36613b47565b90506117b8614630565b60ff60e15416610e625760c9546001600160a01b031633036117fe576117f6906117e061568b565b6117e86156c1565b6117f18161498b565b615667565b600160975580f35b604051634ca8886760e01b8152600490fd5b90503461090357602036600319011261090357610100916001600160a01b03906040908261183c6139bb565b16815260db6020522091825492826001820154168360028301541660038301549160056004850154940154956040519760ff81161515895260ff8160081c16151560208a015260101c1660408801526060870152608086015260a085015260c0840152820152f35b8234610300576060366003190112610300576004356001600160401b03602435818111610fce576118d9903690600401613b17565b9091604435908111611ab7576118f3903690600401613b17565b9290916118fe614630565b60ff60e15416610e625760c95460405163430c208160e01b815233600482015260248101879052602093916001600160a01b03919085908290604490829086165afa90811561170c578991611a9a575b50156117fe57858303611a885761196361568b565b60d3541691604051630794a69b60e21b81528760048201528481602481875afa90811561170c578991611a6b575b50611a5957836024936040519485809263d4e2616f60e01b82528b60048301525afa8015611699576119d2938991611a3c575b5015611a26575b3691613b74565b916119dc84613b5d565b936119ea6040519586613a94565b8085528285019060051b820191368311610d7d57905b828210611a1757876117f6886117f18989836151b7565b81358152908301908301611a00565b611a2e6156c1565b611a37876156f0565b6119cb565b611a539150853d87116117985761178a8183613a94565b896119c4565b604051633b524d7f60e11b8152600490fd5b611a829150853d87116117985761178a8183613a94565b89611991565b60405163512509d360e11b8152600490fd5b611ab19150853d87116117985761178a8183613a94565b8961194e565b8480fd5b8234610300578060031936011261030057606060d45460d55460d6549060405192835260208301526040820152f35b8234610300576060366003190112610300576001600160401b03600435818111610e5e57611b1c903690600401613bca565b906024908135908111610fce57611b37903690600401613c57565b60c95460405163430c208160e01b8152336004820152604480356024830181905290946001600160a01b039491939192909160209082908890829089165afa908115611699578891611c21575b50156117fe578693845b8751811015611c1d5781611ba2828a61473e565b5116611bae828661473e565b5190803b15610d7d57611be79188918a838a604051968795869485936353c2957d60e11b8552600485015260408d8501528301906147da565b03925af1908115610d49578791611c09575b5050611c049061470b565b611b8e565b611c1290613a4a565b610fbb57858a611bf9565b8580f35b611c39915060203d81116117985761178a8183613a94565b88611b84565b823461030057611c4e36613b47565b90611c57614630565b8260018060a01b03918260cb54166040519063ed29fc1160e01b82528160048160209687945af1908115610791578391611cd4575b50505b838110611c9f5784600160975580f35b8083611cad611ccf936139fb565b90549060031b1c16865260da8352611cca84604088205416614a4d565b61470b565b611c8f565b813d8311611cf6575b611ce78183613a94565b81010312610d81578186611c8c565b503d611cdd565b8234610300576020366003190112610300576040602091600435815260dc83522054604051908152f35b8234610300576040366003190112610300576040611d436139d1565b91600435815260df602052209060018060a01b03166000526020526020604060002054604051908152f35b8234610300576020366003190112610300576004356001600160401b03811161090357611d9f903690600401613b17565b611da7614630565b60cb5460405163ed29fc1160e01b8152906020908290600490829088906001600160a01b03165af1801561074157611e0c575b50825b818110611ded5783600160975580f35b80611cca611e02610ecd611e0794868861471a565b614a4d565b611ddd565b602090813d8111611e30575b611e228183613a94565b81010312610d815783611dda565b503d611e18565b82346103005780600319360112610300576020611e52614e7a565b604051908152f35b8234610300576020806003193601126109035760043590811515809203610e5e576000805260658152604060002033600052815260ff6040600020541615611ed2575060ff1960e1541660ff82161760e1557e3fd49cd010bbc93544a605b2f7e82759c9c4c8ab53202063ba094bc4094fe58280a280f35b611edb3361418b565b906000604051611eea81613a79565b604281528281019160603684378151156120775760308353815160019081101561206357607860218401536041905b808211612009575050611fc657611f9293611fa19260489260405196879376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b88860152611f69815180928a60378901910161409c565b8401917001034b99036b4b9b9b4b733903937b6329607d1b60378401525180938684019061409c565b01036028810185520183613a94565b611fc260405192839262461bcd60e51b8452600484015260248301906140bf565b0390fd5b6064836040519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f811660108110156106e4576f181899199a1a9b1b9c1cb0b131b232b360811b901a612038848661417a565b5360041c91801561204d576000190190611f19565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b81526032600452602490fd5b90503461090357602036600319011261090357602091604091600435825283522054604051908152f35b823461030057806003193601126103005760d3546040516001600160a01b039091168152602090f35b823461030057806003193601126103005760c9546040516001600160a01b039091168152602090f35b823461030057610160366003190112610300576004356001600160401b03811161090357612139903690600401613b17565b906001600160401b0360243511610e5e57604060243536036003190112610e5e576001600160401b0360443511610e5e57606060443536036003190112610e5e576001600160401b0360643511610e5e57608060643536036003190112610e5e576001600160401b0360843511610e5e5760a060843536036003190112610e5e5760c03660a3190112610e5e5760ca546001600160a01b03169060a435156129cf576040516370a0823160e01b8152336004820152602081602481865afa9081156107c757859161299e575b50925b80612988575b505060243560040161222081806148af565b905061295d575b5061223c6024604435016044356004016148af565b90506127f9575b612252600460643501806148af565b90506125c9575b60246084350135612502575b60a435612270578280f35b670de0b6b3a76400008060a435116124f0576040516370a0823160e01b81523360048201526020938482602481875afa9182156107915786926124bf575b50906122c06122c59260a43592614686565b61415a565b048015918215806122d8575b5050508280f35b60c9546001600160a01b0316936122f1843033866146b3565b90612452575b156123ee5760405163095ea7b360e01b858201526001600160a01b0384166024820152604481018390526123419161233c82606481015b03601f198101845283613a94565b61436b565b60e4356001600160a01b03811690819003610d81576101043592831515809403610d81576101243592831515809403610d81578660c492879560405197889687956342c15c8760e01b87526004870152863560248701526044860152606485015260848401526101443560a48401525af18015610821576123c5575b8080806122d1565b813d83116123e7575b6123d88183613a94565b81010312610d815781806123bd565b503d6123ce565b60405162461bcd60e51b815260048101859052603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608490fd5b50604051636eb1769f60e11b81523060048201526001600160a01b03841660248201528481604481855afa908115610791578691612492575b50156122f7565b90508481813d83116124b8575b6124a98183613a94565b81010312610d8157518661248b565b503d61249f565b91508482813d83116124e9575b6124d68183613a94565b81010312610d81579051906122c06122ae565b503d6124cc565b604051638de4a0f760e01b8152600490fd5b60d2548390600460843501906001600160a01b03166125208261494c565b9061253e61253260446084350161494c565b936084803501906148af565b919093813b15610fbb5785809461259f604051978896879586946378c16d8760e01b8652336004870152151560248601526024608435013560448601521515606485015260646084350135608485015260c060a485015260c4840191614928565b03925af1801561087c576125b5575b5050612265565b6125be90613a4a565b610e5e5782846125ae565b9190815b6125dc600460643501806148af565b9050811015612661576125f4600460643501806148af565b33916001600160a01b039161260e91610ecd91869161471a565b160361261c576001016125cd565b60405162461bcd60e51b815260206004820152601e60248201527f757365727320636f6e7461696e6573206e6f206f6e6c792063616c6c657200006044820152606490fd5b5060d154919290916001600160a01b0316612681600460643501806148af565b6126986024606494939435016064356004016148af565b9390916126af6044606435016064356004016148af565b6126c260648035016064356004016148af565b949095873b156127f55761270f908c996126fd61272196959460049b9a9860806040519d8e6301c7ba5760e61b8152015260848d01916148e4565b8a81036003190160248c0152916148e4565b87810360031901604489015291614928565b91600319858403016064860152808352602083019060208160051b850101938388915b838310612782575050505050508391838381809403925af1801561087c5761276e575b5050612259565b61277790613a4a565b610e5e578284612767565b91939590929496979850601f198282030186528635601e19843603018112156127f157830190602082359201916001600160401b0381116127ed578060051b360383136127ed576127d96020928392600195614928565b98019601930190918b989796959492612744565b8d80fd5b8c80fd5b8b80fd5b9061283c6128116024604435016044356004016148af565b61283461282a60448095949535016044356004016148af565b9490923691613b74565b923691613be8565b60c95460405163430c208160e01b815233600480830191909152604480359091013560248301529293929091602091839182906001600160a01b03165afa90811561079157869161293f575b50156117fe578492835b8251811015612934576001600160a01b036128ad828561473e565b51166128b9828661473e565b5190803b15612930576128fa87929183926040519485809481936353c2957d60e11b83526044356004013560048401526040602484015260448301906147da565b03925af190811561079157869161291c575b50506129179061470b565b612892565b61292590613a4a565b611ab757848861290c565b8680fd5b509392505050612243565b612957915060203d81116117985761178a8183613a94565b86612888565b8061101f61282a61283461297484612982966148af565b9290946024803501906148af565b83612227565b612997916108fb913691613b74565b838061220e565b90506020813d82116129c7575b816129b860209383613a94565b81010312610d81575185612205565b3d91506129ab565b8392612208565b8234610300576040366003190112610300576129f06139bb565b6129f86139d1565b9082549160ff8360081c161591828093612cee575b8015612cd7575b15612c7b5760ff1984811660011786559383612c6a575b506001600160a01b0391818316918215612c585786926002604360981b0191823b15611ab75784809360646040518098819363c8992e6160e01b835260026004840152600160248401528760448401525af1948515610821578695612c3f575b503b612bf0575b5060ff612aba915460081c16612aa7816142b8565b612ab0816142b8565b60016097556142b8565b3360009081527fffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b602090815260409091205490959060ff1615612ba1575b5016906004846001600160601b0360a01b93808560c954161760c95560405192838092637e062a3560e11b82525afa908115610791578691612b84575b50169060ca54161760ca55610e1060d955612b4e575080f35b7f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989061ff0019835416835560405160018152a180f35b612b9b9150853d871161073a5761072b8183613a94565b86612b35565b600080526065865260406000203360005286526001604060002091825416179055333360007f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a486612af8565b809193503b15610e5e578280916024604051809681936305573b1760e31b83523060048401525af19283156108a857849315612a9257612c3291929350613a4a565b611ab75781908587612a92565b83919550612c4c90613a4a565b61090357849389612a8b565b604051639fabe1c160e01b8152600490fd5b61ffff191661010117855585612a2b565b60405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608490fd5b50303b158015612a145750600160ff851614612a14565b50600160ff851610612a0d565b8234610300578060031936011261030057612d14614630565b60cb5460405163ed29fc1160e01b81526020916001600160a01b039190839082906004908290889087165af1908115610741578391612d79575b505060d45491835b838110612d665784600160975580f35b8083611cad612d74936139fb565b612d56565b813d8311612d9b575b612d8c8183613a94565b81010312610d81578184612d4e565b503d612d82565b8234610300576040366003190112610300576004356001600160401b03811161090357612dd3903690600401613ad0565b612ddb6139d1565b612de3613c72565b6040516020810190612e0d6020828651612e008187858b0161409c565b8101038084520182613a94565b5190207f39eb9ec2059d897c44a17440c762c429de204f6fddd727156ca52b8da086a6f78103612e8b575060cb80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e85905b6040519182916020835260018060a01b03169460208301906140bf565b0390a280f35b7ff23a19003b02ccc6ddd73a13c071e09977c34bfd7b5318a44fe456d9a77dd0af8103612ee7575060cc80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f18c95c463f9590b3f298aef56c7cfb639672452cd99ac8d92a9fc0e2ef46ab558103612f43575060d180546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fbbbfaae454470f56db24caaffaae3a4d3d0ed7a761421871150faa442416ea838103612f9f575060d280546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f8ba8cbf9a47db7b5e8ae6c0bff072ed6faefec4a0722891b09f22b7ac343fd4f8103612ffb575060d380546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fa0238e972eab1b5ee9c4988c955a7165a662b3206031ac6ac27a3066d669a28d8103613057575060cd80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fb8e13a5900588d0607f820e1a839eb41b418c77b9db23e333bcc679d611dbc9b81036130b3575060ce80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7fe8ee2fdef59c2203ee9a363d82083446f25f27a1aff8fc1f0f3f79b83d30305c810361310f575060cf80546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b7f7ebf69e1e15f4a4db2cb161251ab5c47f9f68d65713eba9542fedffbe59b7931036131695760d080546001600160a01b0319166001600160a01b03831617905560008051602061579983398151915290612e8590612e68565b604051634ba4f8e160e01b8152600490fd5b823461030057806003193601126103005760d0546040516001600160a01b039091168152602090f35b9050346109035760203660031901126109035760cb54600435916001600160a01b0391821633036117fe576131e1838360ca5416309033906146b3565b6131e9614e7a565b62093a7f19810190811161327357845260205260408320548061323c575b5060ca5416906040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a380f35b670de0b6b3a76400008084029084820414841517156132735761326a9161326291614693565b60d75461416d565b60d75538613207565b634e487b7160e01b85526011600452602485fd5b82346103005760203660031901126103005760043560d5548110156109035760d56000527f51858de9989bf7441865ebdadbf7382c8838edbf830f5d86a9a51ac773676dd601546040516001600160a01b039091168152602090f35b8234610300576040366003190112610300576132fd6139d1565b336001600160a01b0382160361331957610900906004356140e4565b60405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608490fd5b82346103005760208060031936011261090357600435613394614630565b60ff60e15416610e625760c95460405163430c208160e01b8152336004820152602481018390526001600160a01b039184908290604490829086165afa9081156107c75785916134a5575b50156117fe576133ed61568b565b60d35416604051630794a69b60e21b81528260048201528381602481855afa9081156107c7578591613488575b50611a5957826024916040519283809263d4e2616f60e01b82528660048301525afa908115610741576117f693859261346b575b50501561345e576117f18161498b565b6134666156c1565b6117e8565b6134819250803d106117985761178a8183613a94565b848061344e565b61349f9150843d86116117985761178a8183613a94565b8561341a565b6134bc9150843d86116117985761178a8183613a94565b856133df565b8234610300576020366003190112610300576004356134df614630565b60ff60e15416610e6257806134f48392615724565b6134fd816156f0565b61350561568b565b61350d6156c1565b61351681614eec565b61351f81615667565b60c9546001600160a01b031690813b1561357f57829160448392604051948593849262bcea6b60e31b845260048401528160248401525af1801561087c5761356b575b50600160975580f35b61357490613a4a565b610300578082613562565b5050fd5b823461030057806003193601126103005760d1546040516001600160a01b039091168152602090f35b8234610300576040366003190112610300576004356135c96139d1565b81835260656020526135e16001604085200154613f91565b81600052606560205260406000209060018060a01b0316908160005260205260ff6040600020541615613612578280f35b8160005260656020526040600020816000526020526040600020600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d600080a481808280f35b8234610300578060031936011261030057602060d754604051908152f35b8234610300578060031936011261030057602060d854604051908152f35b82346103005760203660031901126103005760016040602092600435815260658452200154604051908152f35b8234610300578060031936011261030057602060d954604051908152f35b82346103005760203660031901126103005760209060ff906040906001600160a01b036137166139bb565b16815260db8452205460081c166040519015158152f35b823461030057602090816003193601126103005760043560ff60e15416610e625761375781615724565b613760816156f0565b61376861568b565b6137706156c1565b60d354604051631e60c38b60e01b8152600481018390526001600160a01b0394909185168183602481845afa9283156107c75785936138a4575b50803b15611ab7578480916024604051809481936302aba62f60e11b83528960048401525af180156107c75761388e575b5080602493949560c95416604051948580926303d35ae760e51b82528660048301525afa9081156107c757859161385e575b5061382992506138505761382081614eec565b6117f183615667565b7febf777a6065df32120bd289932378b01179d9a5df170d4a43e8ca97e16e17ea08280a280f35b6138598161498b565b613820565b905082813d8311613887575b6138748183613a94565b81010312610d815761382991518561380d565b503d61386a565b6024939461389c8392613a4a565b9493506137db565b9092508181813d83116138cc575b6138bc8183613a94565b81010312611ab7575191866137aa565b503d6138b2565b8234610300576020366003190112610300576040602091600435815260dd83522054604051908152f35b823461030057806003193601126103005760cb546040516001600160a01b039091168152602090f35b8234610300576020366003190112610300576020906001600160a01b0390600290604090836139536139bb565b16815260db855220015416604051908152f35b8390346109035760203660031901126109035760043563ffffffff60e01b8116809103610e5e5760209250637965db0b60e01b81149081156139aa575b5015158152f35b6301ffc9a760e01b149050836139a3565b600435906001600160a01b0382168203610d8157565b602435906001600160a01b0382168203610d8157565b35906001600160a01b0382168203610d8157565b60d4548110156106e45760d46000527f9780e26d96b1f2a9a18ef8fc72d589dbf03ef788137b64f43897e83a91e7feec0190600090565b80548210156106e45760005260206000200190600090565b6001600160401b0381116106fa57604052565b61010081019081106001600160401b038211176106fa57604052565b608081019081106001600160401b038211176106fa57604052565b90601f801991011681019081106001600160401b038211176106fa57604052565b6001600160401b0381116106fa57601f01601f191660200190565b81601f82011215610d8157803590613ae782613ab5565b92613af56040519485613a94565b82845260208383010111610d8157816000926020809301838601378301015290565b9181601f84011215610d81578235916001600160401b038311610d81576020808501948460051b010111610d8157565b6040906003190112610d81576004359060243590565b6001600160401b0381116106fa5760051b60200190565b9291613b7f82613b5d565b91613b8d6040519384613a94565b829481845260208094019160051b8101928311610d8157905b828210613bb35750505050565b838091613bbf846139e7565b815201910190613ba6565b9080601f83011215610d8157816020613be593359101613b74565b90565b92919092613bf584613b5d565b91613c036040519384613a94565b829480845260208094019060051b830192828411610d815780915b848310613c2d57505050505050565b82356001600160401b038111610d81578691613c4c8684938601613bca565b815201920191613c1e565b9080601f83011215610d8157816020613be593359101613be8565b3360009081527f847e30cfc3a73cca7644072137344a252d944ea904fdca79afe80a7c92f5b306602090815260408083205490927fdb77e4a0d65a915fbfe630f3ac05f3a2259b73e7baead93657155a1e625c8b5e9160ff1615613cd65750505050565b613cdf3361418b565b91845190613cec82613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b818311613dde57505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b01036028810187520185613a94565b5192839262461bcd60e51b8452600484015260248301906140bf565b60648486519062461bcd60e51b825280600483015260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a613e0e858761417a565b5360041c928015613e2457600019019190613d1d565b634e487b7160e01b82526011600452602482fd5b634e487b7160e01b83526032600452602483fd5b3360009081527f2ef62106252d7662ea25efd6fda847ba96543a6fb24982c308bdc4485c3fb5fb602090815260408083205490927f71840dc4906352362b0cdaf79870196c8e42acafade72d5d5a6d59291253ceb19160ff1615613eb05750505050565b613eb93361418b565b91845190613ec682613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b818311613f4b57505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a613f7b858761417a565b5360041c928015613e2457600019019190613ef7565b60009080825260209060658252604092838120338252835260ff848220541615613fbb5750505050565b613fc43361418b565b91845190613fd182613a79565b6042825284820192606036853782511561207757603084538251906001918210156120775790607860218501536041915b81831161405657505050613d9c576048611fc2938693613d8093613d71985198899376020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8a860152611f69815180928c60378901910161409c565b909192600f81166010811015613e38576f181899199a1a9b1b9c1cb0b131b232b360811b901a614086858761417a565b5360041c928015613e2457600019019190614002565b60005b8381106140af5750506000910152565b818101518382015260200161409f565b906020916140d88151809281855285808601910161409c565b601f01601f1916010190565b906000918083526065602052604083209160018060a01b03169182845260205260ff60408420541661411557505050565b8083526065602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4565b8181029291811591840414171561204d57565b9190820180921161204d57565b9081518110156106e4570160200190565b60405190606082018281106001600160401b038211176106fa57604052602a82526020820160403682378251156106e4576030905381516001908110156106e457607860218401536029905b80821161422b5750506141e75790565b606460405162461bcd60e51b815260206004820152602060248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152fd5b9091600f81166010811015614284576f181899199a1a9b1b9c1cb0b131b232b360811b901a61425a848661417a565b5360041c91801561426f5760001901906141d7565b60246000634e487b7160e01b81526011600452fd5b60246000634e487b7160e01b81526032600452fd5b90816020910312610d8157516001600160a01b0381168103610d815790565b156142bf57565b60405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608490fd5b60405163a9059cbb60e01b60208201526001600160a01b03909216602483015260448201929092526143519161233c826064810161232e565b565b90816020910312610d8157518015158103610d815790565b60408051908101916001600160a01b03166001600160401b038311828410176106fa576143f9926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af13d15614481573d916143dd83613ab5565b926143eb6040519485613a94565b83523d60008785013e614485565b805190828215928315614469575b505050156144125750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b6144799350820181019101614353565b388281614407565b6060915b919290156144e75750815115614499575090565b3b156144a25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156144fa5750805190602001fd5b60405162461bcd60e51b815260206004820152908190611fc29060248301906140bf565b602081830312610d81578051906001600160401b038211610d81570181601f82011215610d8157805161455081613ab5565b9261455e6040519485613a94565b81845260208284010111610d8157613be5916020808501910161409c565b90614351602f60405180946e02332b734bc102628102332b2b99d1608d1b60208301526145b2815180926020868601910161409c565b810103600f810185520183613a94565b6001600160a01b03918216815291166020820152606060408201819052613be5929101906140bf565b90614351602e60405180946d02332b734bc10213934b132b99d160951b6020830152614620815180926020868601910161409c565b810103600e810185520183613a94565b600260975414614641576002609755565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b9190820391821161204d57565b811561469d570490565b634e487b7160e01b600052601260045260246000fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815260a08101918183106001600160401b038411176106fa576143519260405261436b565b600019811461204d5760010190565b91908110156106e45760051b0190565b356001600160a01b0381168103610d815790565b80518210156106e45760209160051b010190565b906000915b80518310156147d5576001600160a01b03614772848361473e565b511692833b15610d81576000806040956024875180948193630c00007b60e41b83523360048401525af180156147ca576147b4939495506147bb575b5061470b565b9190614757565b6147c490613a4a565b386147ae565b85513d6000823e3d90fd5b509050565b90815180825260208080930193019160005b8281106147fa575050505090565b83516001600160a01b0316855293810193928101926001016147ec565b91909160005b81518110156148a9576001600160a01b03614838828461473e565b5116614844828661473e565b51813b15610d8157600061487e819360409384519586809481936336c3aa6160e21b835233600484015288602484015260448301906147da565b03925af190811561489f57509061489a92916147bb575061470b565b61481d565b513d6000823e3d90fd5b50509050565b903590601e1981360301821215610d8157018035906001600160401b038211610d8157602001918160051b36038313610d8157565b91908082526020809201929160005b828110614901575050505090565b909192938280600192838060a01b03614919896139e7565b168152019501939291016148f3565b81835290916001600160fb1b038311610d815760209260051b809284830137010190565b358015158103610d815790565b9061496382613b5d565b6149706040519182613a94565b8281528092614981601f1991613b5d565b0190602036910137565b9060009180835260209260dc8452604093848220908551808383829554938481520190865283862092865b85828210614a2e575050506149cd92500383613a94565b6149d78251614959565b93835b8351811015614a1e5781855260de8352878520600191906001600160a01b03614a03838861473e565b51168752845288862054614a17828961473e565b52016149da565b50919250506143519394506151b7565b85546001600160a01b03168452600195860195889550930192016149b6565b9060018060a01b03918281169060009382855260209060db8252604090818720938251614a7981613a5d565b85549560ff871615158252838683019760ff8160081c161515895260101c16858301528360018201541660608301528360028201541660808301908152600382015460a084015260e0600560048401549360c0860194855201549301928352614ae0614e7a565b80935110614af6575b5050505050505050509050565b62093a7f198301838111614ccf578b5260df875284868c209151168b528652848a20549081614c60575b505060d754878a5260db8652848a20906004820155600381018054978815159081614c55575b5015614ae9578a90556005015560ca54835163095ea7b360e01b81526001600160a01b03929092166004830152602482018690528490829060449082908c9087165af18015614c4b57614c2e575b5060ca541686853b1561030057825163b66503cf60e01b81526001600160a01b03929092166004830152602482018590528160448183895af18015614c24577f4fa9693cae526341d334e2862ca2413b2e503f1266255f9e0869fb36e6d89b179495969750614c15575b50519283523392a380388080808080808080614ae9565b614c1e90613a4a565b38614bfe565b82513d89823e3d90fd5b614c4490843d86116117985761178a8183613a94565b5038614b94565b83513d8a823e3d90fd5b905051151538614b46565b60d7549051614c6e91614686565b908115614b2057670de0b6b3a764000091614c889161415a565b0486511515600014614cb657878a5260db8652614cac6003868c200191825461416d565b90555b3880614b20565b614cca908460ca54168560cb541690614318565b614caf565b634e487b7160e01b8c52601160045260248cfd5b9192909260d7549260405192614cf884613a5d565b600184526020840190600182526040850194600560018060a01b03948587168852856060840194169889855286608085019b169a8b815260a085019060009788835260c087019384528960e08801958a8752169b8c8a5260db60205260408a20975115159160ff61ff008a549251151560081b169262010000600160b01b03905160101b1693169069ffffffffffffffffffff60b01b1617171786558860018701975116966001600160601b0360a01b97888254161790558860028701915116878254161790555160038501555160048401555191015586825260da60205284604083209182541617905560d45490600160401b821015614e66575091614e6191614e2a8460017fa4d97e9e7c65249b4cd01acb82add613adea98af32daf092366982f0a0d4e453960160d4556139fb565b815460039190911b92831b19169188901b919091179055604080513381526001600160a01b03909216602083015290918291820190565b0390a4565b634e487b7160e01b81526041600452602490fd5b60cb54604051631a2732c160e31b815290602090829060049082906001600160a01b03165afa908115614ee057600091614eb2575090565b906020823d8211614ed8575b81614ecb60209383613a94565b8101031261030057505190565b3d9150614ebe565b6040513d6000823e3d90fd5b908160005260dc60205260406000206040518082602082945493848152019060005260206000209260005b818110615195575050614f2c92500382613a94565b600090614f37614e7a565b8460005260dd6020526040600020549160005b81518110156150fb576001600160a01b03614f65828461473e565b51168760005260de90816020526040600020816000526020526040600020549182614f9c575b505050614f979061470b565b614f4a565b896000526020526040600020816000526020526000604081205584861015614fc5575b80614f8b565b8460005260df6020526040600020816000526020526040600020614fea838254614686565b905560005260da60205260018060a01b03604060002054168060005260db60205260018060a01b0360406000205460101c16803b15610d81576000809160448c6040519485938492630441a3e760e41b845289600485015260248401525af18015614ee0576150ec575b50600081815260db60205260409020600101546001600160a01b0316803b15610d81576000809160448c6040519485938492630441a3e760e41b845289600485015260248401525af18015614ee0576150dd575b5060005260db60205260ff60406000205460081c166150c8575b80614fbf565b6150d690614f97929661416d565b94906150c2565b6150e690613a4a565b386150a8565b6150f590613a4a565b38615054565b505080939492911015615175575b8160005260dc60205260406000208054906000815581615154575b50506040519081527fad26e79d8a0938a06f8edf8d669ea711acf484fee628338db569984c46299aad60203392a4565b6000526020600020908101905b818110156151245760008155600101615161565b8260005260e0602052604060002061518e828254614686565b9055615109565b84546001600160a01b0316835260019485019486945060209093019201614f17565b90602492916151c581614eec565b60c9546040516339f890b560e21b8152600481018390529460209186919082906001600160a01b03165afa938415614ee057600094615633575b506000948594865b84518810156152e1576001600160a01b03615222898761473e565b511660005260da60205260018060a01b036040600020541660005260db602052604060002060405161525381613a5d565b815460ff80821615158352600882901c16158015602084015260109190911c6001600160a01b0390811660408401526001840154811660608401526002840154166080830152600383015460a0830152600483015460c083015260059092015460e090910152611355576152d56152db916152ce8a8961473e565b519061416d565b9761470b565b96615207565b90939194929596506152f1614e7a565b906152fc8351614959565b946000985b84518a101561551d57859697989960018060a01b03615320828861473e565b51166001600160a01b03615334838961473e565b511660005260da602052615369856153648661535e8660018060a01b03604060002054169d61473e565b5161415a565b614693565b92831561550b578c60005260de6020526040600020826000526020526040600020546154f9578c60005260dc602052604060002090815492600160401b8410156106fa578e6153c2858895600161542298018155613a32565b81549060031b9084821b9160018060a01b03901b191617905560005260de602052604060002081600052602052826040600020558860005260df602052604060002090600052602052604060002061541b83825461416d565b905561416d565b968261542e838c61473e565b52600081815260db602052604090205460101c6001600160a01b0316803b15610d81576000809160448f6040519485938492631c57762b60e31b84528a600485015260248401525af18015614ee0576154ea575b50600090815260db60205260409020600101546001600160a01b031691823b15610d81578b6044600092836040519687948593631c57762b60e31b8552600485015260248401525af1918215614ee0576154e0926147bb575061470b565b9897969594615301565b6154f390613a4a565b38615482565b6040516308595bd160e41b8152600490fd5b604051630dc5e4d360e01b8152600490fd5b939450505094939550801515806155db575b8560005260e0602052604060002061554883825461416d565b9055615556575b5050505050565b61556b604051926060845260608401906147da565b82810360208401526020808551928381520194019060005b8181106155c557505050907f8eb57ab77951661aa38ed1c05093da478521c8ea49e1a28b88cac6f98b79cf669160408201528033930390a4388080808061554f565b8251865260209586019590920191600101615583565b60c9546001600160a01b0316803b15610d81576000809160446040518094819362bcea6b60e31b83528b6004840152600160248401525af18015614ee057615624575b5061552f565b61562d90613a4a565b3861561e565b90936020823d60201161565f575b8161564e60209383613a94565b8101031261030057505192386151ff565b3d9150615641565b61566f614e7a565b906001820180921161204d5760005260dd602052604060002055565b6156a661569d62093a80420642614686565b60d9549061416d565b4211156156af57565b6040516329f7110f60e21b8152600490fd5b62093a806156d181420642614686565b90810180911161204d5760d9546156e791614686565b4210156156af57565b60005260dd60205261570a60406000205460d8549061416d565b421061571257565b60405163e119bce960e01b8152600490fd5b60c9546001600160a01b03163381900361573c575050565b60405163430c208160e01b81523360048201526024810192909252602090829060449082905afa908115614ee05760009161577a575b50156117fe57565b615792915060203d81116117985761178a8183613a94565b3861577256feb0377b4035c2bd5b6aeb3eb7f139162c730ea47b957e4354f8a81ebb99573deca164736f6c6343000813000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000072e47b1eaaaac6c07ea4071f1d0d355f603e1cc1
-----Decoded View---------------
Arg [0] : blastGovernor_ (address): 0x72e47b1eaAAaC6c07Ea4071f1d0d355f603E1cc1
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000072e47b1eaaaac6c07ea4071f1d0d355f603e1cc1
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.