ETH Price: $2,939.89 (-0.60%)

Contract

0x53500A5923fB177a6f6e38991F518D49C113e9AC
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VoterUpgradeableV2

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion
File 1 of 37 : VoterUpgradeableV2.sol
// 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();
            }
        }
    }
}

File 2 of 37 : IAlgebraFactory.sol
// 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);
}

File 26 of 37 : IVoter.sol
// 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;
}

File 28 of 37 : LibVoterErrors.sol
// 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);
}

File 32 of 37 : BlastGovernorClaimableSetup.sol
// 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;
}

Settings
{
  "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

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"}]

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.