Sonic Blaze Testnet

Contract

0x88059E4DA489c59B0610F0a88f10C5052aBC8d8C

Overview

S Balance

Sonic Blaze LogoSonic Blaze LogoSonic Blaze Logo0 S

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Voter

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 100000 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 7 : Voter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
import { ReentrancyGuard } from "solady/utils/ReentrancyGuard.sol";
import { IVotingEscrow } from "./interfaces/IVotingEscrow.sol";

/// @title Voter contract
/// @author Rings Protocol
/// @notice This contract allows users to vote on gauges to distribute rewards
/// it is a fork of Thena's Voter contract
contract Voter is Ownable2Step, ReentrancyGuard {
    using SafeTransferLib for address;

    /*//////////////////////////////////////////////////////////////
                                 STRUCTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Vote struct
     * @param weight The weight of the vote
     * @param votes The votes amount
     */
    struct Vote {
        uint256 weight;
        uint256 votes;
    }

    /**
     * @notice Gauge status struct
     * @param isGauge The gauge status
     * @param isAlive The gauge status
     */
    struct GaugeStatus {
        bool isGauge;
        bool isAlive;
    }

    /**
     * @notice Casted vote struct
     * @param gauge The gauge address
     * @param weight The weight of the vote
     * @param votes The votes amount
     */
    struct CastedVote {
        address gauge;
        uint256 weight;
        uint256 votes;
    }

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    /// @notice Emitted when a gauge is added
    event GaugeAdded(address indexed gauge, uint256 cap);
    /// @notice Emitted when a gauge is killed
    event GaugeKilled(address indexed gauge);
    /// @notice Emitted when a gauge is revived
    event GaugeRevived(address indexed gauge);
    /// @notice Emitted when a gauge cap is updated
    event GaugeCapUpdated(address indexed gauge, uint256 newCap);
    /// @notice Emitted when a vote is casted
    event Voted(
        address indexed voter, uint256 indexed tokenId, address indexed gauge, uint256 ts, uint256 weight, uint256 votes
    );
    /// @notice Emitted when a vote is reseted
    event VoteReseted(address indexed voter, uint256 indexed tokenId, address indexed gauge);
    /// @notice Emitted when a budget is deposited
    event BudgetDeposited(address indexed depositor, uint256 indexed period, uint256 amount);
    /// @notice Emitted when a reward is claimed
    event RewardClaimed(address indexed gauge, uint256 amount);
    /// @notice Emitted when the vote delay is updated
    event VoteDelayUpdated(uint256 oldVoteDelay, uint256 newVoteDelay);
    /// @notice Emitted when the deposit freeze is triggered
    event DepositFreezeTriggered(bool frozen);
    /// @notice Emitted when the default gauge cap is updated
    event DefaultCapUpdated(uint256 newCap);

    /*//////////////////////////////////////////////////////////////
                                 ERRORS
    //////////////////////////////////////////////////////////////*/

    error InvalidParameter();
    error NullAmount();
    error ZeroAddress();
    error ArrayLengthMismatch();
    error MaxArraySizeExceeded();
    error GaugeNotListed();
    error GaugeAlreadyListed();
    error KilledGauge();
    error GaugeAlreadyKilled();
    error GaugeNotKilled();
    error VoteDelayNotExpired();
    error CannotVoteWithNft();
    error NoVotingPower();
    error VoteWeightOverflow();
    error NoVoteToRecast();
    error DepositFrozen();
    error InvalidCap();

    /*//////////////////////////////////////////////////////////////
                                 CONSTANTS
    //////////////////////////////////////////////////////////////*/

    uint256 public constant PERIOD_DURATION = 7 days;
    uint256 private constant WEEK = 86_400 * 7;
    uint256 private constant UNIT = 1e18;
    uint256 private constant MAX_TOKEN_ID_LENGTH = 10;
    uint256 private constant MAX_WEIGHT = 10_000; // 100% in BPS

    address public immutable ve;
    address public immutable baseAsset;

    /*//////////////////////////////////////////////////////////////
                            MUTABLE VARIABLES
    //////////////////////////////////////////////////////////////*/

    bool public isDepositFrozen;
    address[] public gauges;

    uint256 public voteDelay = 1 hours; // To prevent spamming votes

    uint256 public defaultCap = 0.1e18;

    // timestamp => budget amount
    mapping(uint256 => uint256) public periodBudget;
    // gauge => index
    mapping(address => uint256) public gaugeIndex;
    // gauge => label
    mapping(address => string) public gaugeLabel;
    // gauge => cap
    mapping(address => uint256) public gaugeCaps;
    // gauge => next period the gauge can claim rewards
    mapping(address => uint256) public gaugesDistributionTimestamp;
    // nft => timestamp => gauge => votes
    mapping(uint256 => mapping(uint256 => mapping(address => Vote))) public votes;
    // nft => timestamp => gauges
    mapping(uint256 => mapping(uint256 => address[])) public gaugeVote;
    // timestamp => gauge => votes
    mapping(uint256 => mapping(address => uint256)) public votesPerPeriod;
    // timestamp => total votes
    mapping(uint256 => uint256) public totalVotesPerPeriod;
    // nft => timestamp
    mapping(uint256 => uint256) public lastVoted;
    // nft => timestamp => bool
    mapping(uint256 => mapping(uint256 => bool)) public voteCastedPeriod;
    // gauge => status (isAlive and isGauge)
    mapping(address => GaugeStatus) public gaugeStatus;

    /*//////////////////////////////////////////////////////////////
                              CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner, address _ve, address _baseAsset) Ownable(_owner) {
        ve = _ve;
        baseAsset = _baseAsset;
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the current period timestamp
     * @return uint256
     */
    function currentPeriod() public view returns (uint256) {
        return (block.timestamp / WEEK) * WEEK;
    }

    /**
     * @notice Returns the number of gauges
     * @return uint256
     */
    function gaugesCount() external view returns (uint256) {
        return gauges.length;
    }

    /**
     * @notice Returns true if the gauge is listed
     * @return bool
     */
    function isGauge(address gauge) external view returns (bool) {
        return gaugeStatus[gauge].isGauge;
    }

    /**
     * @notice Returns true if the gauge is alive
     * @return bool
     */
    function isAlive(address gauge) external view returns (bool) {
        return gaugeStatus[gauge].isAlive;
    }

    /**
     * @notice Returns the casted voted for a specific NFT
     * @param tokenId The NFT id
     * @return CastedVote[] memory
     */
    function getNftCurrentVotes(uint256 tokenId) external view returns (CastedVote[] memory) {
        uint256 nextPeriod = currentPeriod() + WEEK;
        address[] memory _gauges = gaugeVote[tokenId][nextPeriod];
        uint256 length = _gauges.length;
        CastedVote[] memory _votes = new CastedVote[](length);

        for (uint256 i = 0; i < length; ++i) {
            address gauge = _gauges[i];
            Vote memory voteData = votes[tokenId][nextPeriod][gauge];
            _votes[i] = CastedVote(gauge, voteData.weight, voteData.votes);
        }

        return _votes;
    }

    /**
     * @notice Returns the casted voted for a specific NFT at a specific period
     * @param tokenId The NFT id
     * @param ts The timestamp
     * @return CastedVote[] memory
     */
    function getNftCurrentVotesAtPeriod(uint256 tokenId, uint256 ts) external view returns (CastedVote[] memory) {
        ts = (ts / WEEK) * WEEK;
        address[] memory _gauges = gaugeVote[tokenId][ts];
        uint256 length = _gauges.length;
        CastedVote[] memory _votes = new CastedVote[](length);

        for (uint256 i = 0; i < length; ++i) {
            address gauge = _gauges[i];
            Vote memory voteData = votes[tokenId][ts][gauge];
            _votes[i] = CastedVote(gauge, voteData.weight, voteData.votes);
        }

        return _votes;
    }

    /**
     * @notice Returns the total votes for the current period
     * @return uint256
     */
    function getTotalVotes() external view returns (uint256) {
        return totalVotesPerPeriod[currentPeriod() + WEEK];
    }

    /**
     * @notice Returns the votes for a specific gauge for the current period
     * @param gauge The gauge address
     * @return uint256
     */
    function getGaugeVotes(address gauge) external view returns (uint256) {
        return votesPerPeriod[currentPeriod() + WEEK][gauge];
    }

    /**
     * @notice Returns the votes for a specific NFT on a specific gauge for the current period
     * @param tokenId The NFT id
     * @param gauge The gauge address
     * @return uint256
     */
    function getNftVotesOnGauge(uint256 tokenId, address gauge) external view returns (uint256) {
        return votes[tokenId][currentPeriod() + WEEK][gauge].votes;
    }

    /**
     * @notice Returns the total votes for a specific period
     * @param ts The timestamp
     * @return uint256
     */
    function getTotalVotesAtPeriod(uint256 ts) external view returns (uint256) {
        ts = (ts / WEEK) * WEEK;
        return totalVotesPerPeriod[ts];
    }

    /**
     * @notice Returns the votes for a specific gauge for a specific period
     * @param gauge The gauge address
     * @param ts The timestamp
     * @return uint256
     */
    function getGaugeVotesAtPeriod(address gauge, uint256 ts) external view returns (uint256) {
        ts = (ts / WEEK) * WEEK;
        return votesPerPeriod[ts][gauge];
    }

    /**
     * @notice Returns the votes for a specific NFT on a specific gauge for a specific period
     * @param tokenId The NFT id
     * @param gauge The gauge address
     * @param ts The timestamp
     * @return uint256
     */
    function getNftVotesOnGaugeAtPeriod(uint256 tokenId, address gauge, uint256 ts) external view returns (uint256) {
        ts = (ts / WEEK) * WEEK;
        return votes[tokenId][ts][gauge].votes;
    }

    /**
     * @notice Returns the relative weight of a gauge for the current period
     * @param gauge The gauge address
     * @return uint256
     */
    function getGaugeRelativeWeight(address gauge) external view returns (uint256) {
        return _getGaugeRelativeWeight(gauge, currentPeriod() + WEEK);
    }

    /**
     * @notice Returns the relative weight of a gauge for a specific period
     * @param gauge The gauge address
     * @param ts The timestamp
     * @return uint256
     */
    function getGaugeRelativeWeightAtPeriod(address gauge, uint256 ts) external view returns (uint256) {
        ts = (ts / WEEK) * WEEK;
        return _getGaugeRelativeWeight(gauge, ts);
    }

    /**
     * @notice Returns the cap of a gauge (returns the default cap if the gauge cap is 0)
     * @param gauge The gauge address
     * @return uint256
     */
    function getGaugeCap(address gauge) public view returns (uint256) {
        uint256 cap = gaugeCaps[gauge];
        return cap == 0 ? defaultCap : cap;
    }

    /*//////////////////////////////////////////////////////////////
                            PUBLIC FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Vote on gauges for a specific NFT
     * @param tokenId The NFT id
     * @param gaugeList The list of gauges to vote on
     * @param weights The weights for each gauge
     */
    function vote(uint256 tokenId, address[] calldata gaugeList, uint256[] calldata weights) public nonReentrant {
        _voteDelay(tokenId);
        if (!IVotingEscrow(ve).isVotingApprovedOrOwner(msg.sender, tokenId)) revert CannotVoteWithNft();
        if (gaugeList.length != weights.length) revert ArrayLengthMismatch();
        _reset(msg.sender, tokenId);
        _vote(msg.sender, tokenId, gaugeList, weights);

        lastVoted[tokenId] = block.timestamp;
    }

    /**
     * @notice Reset the votes for a specific NFT
     * @param tokenId The NFT id
     */
    function reset(uint256 tokenId) public nonReentrant {
        _voteDelay(tokenId);
        if (!IVotingEscrow(ve).isVotingApprovedOrOwner(msg.sender, tokenId)) revert CannotVoteWithNft();
        _reset(msg.sender, tokenId);
        IVotingEscrow(ve).abstain(tokenId);

        lastVoted[tokenId] = block.timestamp;
    }

    /**
     * @notice Recast the votes for a specific NFT
     * @param tokenId The NFT id
     */
    function recast(uint256 tokenId) public nonReentrant {
        _voteDelay(tokenId);
        if (!IVotingEscrow(ve).isVotingApprovedOrOwner(msg.sender, tokenId)) revert CannotVoteWithNft();

        address[] memory _gauges = gaugeVote[tokenId][currentPeriod()];
        uint256 length = _gauges.length;
        if (length == 0) revert NoVoteToRecast();
        uint256[] memory weights = new uint256[](length);
        for (uint256 i; i < length; ++i) {
            weights[i] = votes[tokenId][currentPeriod()][_gauges[i]].weight;
        }
        _reset(msg.sender, tokenId);
        _vote(msg.sender, tokenId, _gauges, weights);

        lastVoted[tokenId] = block.timestamp;
    }

    /**
     * @notice Vote on gauges for multiple NFTs
     * @param tokenIds The list of NFT ids
     * @param gaugeList The list of gauges to vote on
     * @param weights The weights for each gauge
     */
    function voteMultiple(uint256[] calldata tokenIds, address[] calldata gaugeList, uint256[] calldata weights)
        external
    {
        uint256 length = tokenIds.length;
        if (length > MAX_TOKEN_ID_LENGTH) revert MaxArraySizeExceeded();
        for (uint256 i; i < length; ++i) {
            vote(tokenIds[i], gaugeList, weights);
        }
    }

    /**
     * @notice Reset the votes for multiple NFTs
     * @param tokenIds The list of NFT ids
     */
    function resetMultiple(uint256[] calldata tokenIds) external {
        uint256 length = tokenIds.length;
        if (length > MAX_TOKEN_ID_LENGTH) revert MaxArraySizeExceeded();
        for (uint256 i; i < length; ++i) {
            reset(tokenIds[i]);
        }
    }

    /**
     * @notice Recast the votes for multiple NFTs
     * @param tokenIds The list of NFT ids
     */
    function recastMultiple(uint256[] calldata tokenIds) external {
        uint256 length = tokenIds.length;
        if (length > MAX_TOKEN_ID_LENGTH) revert MaxArraySizeExceeded();
        for (uint256 i; i < length; ++i) {
            recast(tokenIds[i]);
        }
    }

    /**
     * @notice Deposit budget for a specific period
     * @param amount The amount to deposit
     */
    function depositBudget(uint256 amount) external nonReentrant {
        if (amount == 0) revert NullAmount();
        if (isDepositFrozen) revert DepositFrozen();

        baseAsset.safeTransferFrom(msg.sender, address(this), amount);

        uint256 depositPeriod = (currentPeriod() + (WEEK * 2));
        periodBudget[depositPeriod] += amount;

        emit BudgetDeposited(msg.sender, depositPeriod, amount);
    }

    /**
     * @notice Claim rewards for a specific gauge
     * @param gauge The gauge address
     * @return claimedAmount The claimed amount
     */
    function claimGaugeRewards(address gauge) external nonReentrant returns (uint256 claimedAmount) {
        uint256 _currentPeriod = currentPeriod();
        // Fetch the next period the gauge can claim rewards, from the last time it claimed.
        uint256 period = gaugesDistributionTimestamp[gauge];
        while (period <= _currentPeriod) {
            uint256 cap = getGaugeCap(gauge);
            uint256 relativeWeight = _getGaugeRelativeWeight(gauge, period);
            if (relativeWeight > cap) {
                uint256 excessWeight = relativeWeight - cap;
                relativeWeight = cap;

                // If the weight exceeds the cap for the gauge, take back the excess
                // budget, and recycle it.
                uint256 excessAmount = (excessWeight * periodBudget[period]) / UNIT;
                uint256 depositPeriod = (currentPeriod() + (WEEK * 2));
                periodBudget[depositPeriod] += excessAmount;
            }

            claimedAmount += (relativeWeight * periodBudget[period]) / UNIT;
            period += WEEK;
        }

        // Next time the gauge can claim will be after the current period vote is over.
        gaugesDistributionTimestamp[gauge] = _currentPeriod + WEEK;

        if (claimedAmount > 0) {
            baseAsset.safeTransfer(gauge, claimedAmount);

            emit RewardClaimed(gauge, claimedAmount);
        }
    }

    /*//////////////////////////////////////////////////////////////
                            OWNER FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Add a gauge to the list
     * @param gauge The gauge address
     * @param label The gauge label
     * @param cap The gauge cap (can be 0 to use the default cap)
     * @return index The gauge index
     *
     * @custom:require onlyOwner
     */
    function addGauge(address gauge, string memory label, uint256 cap) external onlyOwner returns (uint256 index) {
        if (gauge == address(0)) revert ZeroAddress();
        GaugeStatus storage status = gaugeStatus[gauge];
        if (status.isGauge) revert GaugeAlreadyListed();
        if (cap > UNIT) revert InvalidCap();

        status.isGauge = true;
        status.isAlive = true;

        index = gauges.length;
        gauges.push(gauge);

        gaugeIndex[gauge] = index;
        gaugeLabel[gauge] = label;
        gaugeCaps[gauge] = cap;

        uint256 _currentPeriod = currentPeriod();
        gaugesDistributionTimestamp[gauge] = _currentPeriod;

        emit GaugeAdded(gauge, cap);
    }

    /**
     * @notice Remove a gauge from the list
     * @param gauge The gauge address
     *
     * @custom:require onlyOwner
     */
    function killGauge(address gauge) external onlyOwner {
        if (gauge == address(0)) revert ZeroAddress();
        GaugeStatus storage status = gaugeStatus[gauge];
        if (!status.isGauge) revert GaugeNotListed();
        if (!status.isAlive) revert GaugeAlreadyKilled();
        status.isAlive = false;

        emit GaugeKilled(gauge);
    }

    /**
     * @notice Revive a gauge that has been killed previously
     * @param gauge The gauge address
     *
     * @custom:require onlyOwner
     */
    function reviveGauge(address gauge) external onlyOwner {
        if (gauge == address(0)) revert ZeroAddress();
        GaugeStatus storage status = gaugeStatus[gauge];
        if (!status.isGauge) revert GaugeNotListed();
        if (status.isAlive) revert GaugeNotKilled();
        status.isAlive = true;

        emit GaugeRevived(gauge);
    }

    /**
     * @notice Update the cap of a gauge
     * @param gauge The gauge address
     * @param cap The gauge cap (can be 0 to use the default cap)
     *
     * @custom:require onlyOwner
     *
     * @dev Need to call claimGaugeRewards() before the update to avoid any cases were the gauge
     * ends up claiming more or less than supposed on past periods
     */
    function updateGaugeCap(address gauge, uint256 cap) external onlyOwner {
        if (gauge == address(0)) revert ZeroAddress();
        GaugeStatus storage status = gaugeStatus[gauge];
        if (!status.isGauge) revert GaugeNotListed();
        if (!status.isAlive) revert KilledGauge();
        if (cap > UNIT) revert InvalidCap();

        gaugeCaps[gauge] = cap;

        emit GaugeCapUpdated(gauge, cap);
    }

    /**
     * @notice Update the vote delay
     * @param newVoteDelay The new vote delay
     *
     * @custom:require onlyOwner
     */
    function updateVoteDelay(uint256 newVoteDelay) external onlyOwner {
        if (newVoteDelay >= PERIOD_DURATION) revert InvalidParameter();

        uint256 oldVoteDelay = voteDelay;
        voteDelay = newVoteDelay;

        emit VoteDelayUpdated(oldVoteDelay, newVoteDelay);
    }

    /**
     * @notice Trigger the deposit freeze in case of emergency
     *
     * @custom:require onlyOwner
     */
    function triggerDepositFreeze() external onlyOwner {
        isDepositFrozen = !isDepositFrozen;

        emit DepositFreezeTriggered(isDepositFrozen);
    }

    function updatedefaultCap(uint256 _defaultCap) external onlyOwner {
        defaultCap = _defaultCap;

        emit DefaultCapUpdated(_defaultCap);
    }

    /*//////////////////////////////////////////////////////////////
                            INTERNAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function _voteDelay(uint256 tokenId) internal view {
        if (block.timestamp <= lastVoted[tokenId] + voteDelay) revert VoteDelayNotExpired();
    }

    function _getGaugeRelativeWeight(address gauge, uint256 period) internal view returns (uint256) {
        GaugeStatus memory status = gaugeStatus[gauge];
        if (!status.isGauge || !status.isAlive) return 0;
        uint256 totalVotes = totalVotesPerPeriod[period];
        if (totalVotes == 0) return 0;
        uint256 gaugeVotes = votesPerPeriod[period][gauge];
        return (gaugeVotes * UNIT) / totalVotes;
    }

    function _reset(address voter, uint256 tokenId) internal {
        uint256 nextPeriod = currentPeriod() + WEEK;
        if (voteCastedPeriod[tokenId][nextPeriod]) {
            // otherwise, no vote casted for that period yet, nothing to reset
            address[] memory _gauges = gaugeVote[tokenId][nextPeriod];
            uint256 length = _gauges.length;
            uint256 totalVotesRemoved;
            for (uint256 i; i < length; ++i) {
                address gauge = _gauges[i];
                uint256 voteAmount = votes[tokenId][nextPeriod][gauge].votes;
                votesPerPeriod[nextPeriod][gauge] -= voteAmount;
                totalVotesRemoved += voteAmount;
                delete votes[tokenId][nextPeriod][gauge];

                emit VoteReseted(voter, tokenId, gauge);
            }
            delete gaugeVote[tokenId][nextPeriod];
            totalVotesPerPeriod[nextPeriod] -= totalVotesRemoved;
        }
        voteCastedPeriod[tokenId][nextPeriod] = false;
    }

    function _vote(address voter, uint256 tokenId, address[] memory gaugeList, uint256[] memory weights) internal {
        uint256 nextPeriod = currentPeriod() + WEEK;
        uint256 length = gaugeList.length;

        uint256 _votes = IVotingEscrow(ve).balanceOfNFT(tokenId);
        if (_votes == 0) revert NoVotingPower();
        uint256 totalUsedWeights;
        uint256 totalVotesCasted;

        for (uint256 i; i < length; ++i) {
            address gauge = gaugeList[i];
            if (gauge == address(0)) revert ZeroAddress();
            GaugeStatus memory status = gaugeStatus[gauge];
            if (!status.isGauge) revert GaugeNotListed();
            if (!status.isAlive) revert KilledGauge();

            uint256 gaugeVotes = (_votes * weights[i]) / MAX_WEIGHT;
            totalUsedWeights += weights[i];
            totalVotesCasted += gaugeVotes;

            gaugeVote[tokenId][nextPeriod].push(gauge);

            votesPerPeriod[nextPeriod][gauge] += gaugeVotes;
            votes[tokenId][nextPeriod][gauge] = Vote(weights[i], gaugeVotes);

            emit Voted(voter, tokenId, gauge, block.timestamp, weights[i], gaugeVotes);
        }

        if (totalUsedWeights > MAX_WEIGHT) revert VoteWeightOverflow();
        if (totalUsedWeights > 0) IVotingEscrow(ve).voting(tokenId);

        totalVotesPerPeriod[nextPeriod] += totalVotesCasted;

        voteCastedPeriod[tokenId][nextPeriod] = true;
    }
}

File 2 of 7 : Ownable2Step.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.20;

import {Ownable} from "./Ownable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

File 3 of 7 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Permit2 operations from (https://github.com/Uniswap/permit2/blob/main/src/libraries/Permit2Lib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /// @dev The Permit2 operation has failed.
    error Permit2Failed();

    /// @dev The Permit2 amount must be less than `2**160 - 1`.
    error Permit2AmountOverflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /// @dev The unique EIP-712 domain domain separator for the DAI token contract.
    bytes32 internal constant DAI_DOMAIN_SEPARATOR =
        0xdbb8cf42e1ecb028be3f3dbc922e1d878b963f411dc388ced501601c60f7c6f7;

    /// @dev The address for the WETH9 contract on Ethereum mainnet.
    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    /// @dev The canonical Permit2 address.
    /// [Github](https://github.com/Uniswap/permit2)
    /// [Etherscan](https://etherscan.io/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)
    address internal constant PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function trySafeTransferFrom(address token, address from, address to, uint256 amount)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            success :=
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul( // The arguments of `mul` are evaluated from right to left.
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// If the initial attempt fails, try to use Permit2 to transfer the token.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for the current contract to manage.
    function safeTransferFrom2(address token, address from, address to, uint256 amount) internal {
        if (!trySafeTransferFrom(token, from, to, amount)) {
            permit2TransferFrom(token, from, to, amount);
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to` via Permit2.
    /// Reverts upon failure.
    function permit2TransferFrom(address token, address from, address to, uint256 amount)
        internal
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(add(m, 0x74), shr(96, shl(96, token)))
            mstore(add(m, 0x54), amount)
            mstore(add(m, 0x34), to)
            mstore(add(m, 0x20), shl(96, from))
            // `transferFrom(address,address,uint160,address)`.
            mstore(m, 0x36c78516000000000000000000000000)
            let p := PERMIT2
            let exists := eq(chainid(), 1)
            if iszero(exists) { exists := iszero(iszero(extcodesize(p))) }
            if iszero(and(call(gas(), p, 0, add(m, 0x10), 0x84, codesize(), 0x00), exists)) {
                mstore(0x00, 0x7939f4248757f0fd) // `TransferFromFailed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(iszero(shr(160, amount))))), 0x04)
            }
        }
    }

    /// @dev Permit a user to spend a given amount of
    /// another user's tokens via native EIP-2612 permit if possible, falling
    /// back to Permit2 if native permit fails or is not implemented on the token.
    function permit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        bool success;
        /// @solidity memory-safe-assembly
        assembly {
            for {} shl(96, xor(token, WETH9)) {} {
                mstore(0x00, 0x3644e515) // `DOMAIN_SEPARATOR()`.
                if iszero(
                    and( // The arguments of `and` are evaluated from right to left.
                        lt(iszero(mload(0x00)), eq(returndatasize(), 0x20)), // Returns 1 non-zero word.
                        // Gas stipend to limit gas burn for tokens that don't refund gas when
                        // an non-existing function is called. 5K should be enough for a SLOAD.
                        staticcall(5000, token, 0x1c, 0x04, 0x00, 0x20)
                    )
                ) { break }
                // After here, we can be sure that token is a contract.
                let m := mload(0x40)
                mstore(add(m, 0x34), spender)
                mstore(add(m, 0x20), shl(96, owner))
                mstore(add(m, 0x74), deadline)
                if eq(mload(0x00), DAI_DOMAIN_SEPARATOR) {
                    mstore(0x14, owner)
                    mstore(0x00, 0x7ecebe00000000000000000000000000) // `nonces(address)`.
                    mstore(add(m, 0x94), staticcall(gas(), token, 0x10, 0x24, add(m, 0x54), 0x20))
                    mstore(m, 0x8fcbaf0c000000000000000000000000) // `IDAIPermit.permit`.
                    // `nonces` is already at `add(m, 0x54)`.
                    // `1` is already stored at `add(m, 0x94)`.
                    mstore(add(m, 0xb4), and(0xff, v))
                    mstore(add(m, 0xd4), r)
                    mstore(add(m, 0xf4), s)
                    success := call(gas(), token, 0, add(m, 0x10), 0x104, codesize(), 0x00)
                    break
                }
                mstore(m, 0xd505accf000000000000000000000000) // `IERC20Permit.permit`.
                mstore(add(m, 0x54), amount)
                mstore(add(m, 0x94), and(0xff, v))
                mstore(add(m, 0xb4), r)
                mstore(add(m, 0xd4), s)
                success := call(gas(), token, 0, add(m, 0x10), 0xe4, codesize(), 0x00)
                break
            }
        }
        if (!success) simplePermit2(token, owner, spender, amount, deadline, v, r, s);
    }

    /// @dev Simple permit on the Permit2 contract.
    function simplePermit2(
        address token,
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40)
            mstore(m, 0x927da105) // `allowance(address,address,address)`.
            {
                let addressMask := shr(96, not(0))
                mstore(add(m, 0x20), and(addressMask, owner))
                mstore(add(m, 0x40), and(addressMask, token))
                mstore(add(m, 0x60), and(addressMask, spender))
                mstore(add(m, 0xc0), and(addressMask, spender))
            }
            let p := mul(PERMIT2, iszero(shr(160, amount)))
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x5f), // Returns 3 words: `amount`, `expiration`, `nonce`.
                    staticcall(gas(), p, add(m, 0x1c), 0x64, add(m, 0x60), 0x60)
                )
            ) {
                mstore(0x00, 0x6b836e6b8757f0fd) // `Permit2Failed()` or `Permit2AmountOverflow()`.
                revert(add(0x18, shl(2, iszero(p))), 0x04)
            }
            mstore(m, 0x2b67b570) // `Permit2.permit` (PermitSingle variant).
            // `owner` is already `add(m, 0x20)`.
            // `token` is already at `add(m, 0x40)`.
            mstore(add(m, 0x60), amount)
            mstore(add(m, 0x80), 0xffffffffffff) // `expiration = type(uint48).max`.
            // `nonce` is already at `add(m, 0xa0)`.
            // `spender` is already at `add(m, 0xc0)`.
            mstore(add(m, 0xe0), deadline)
            mstore(add(m, 0x100), 0x100) // `signature` offset.
            mstore(add(m, 0x120), 0x41) // `signature` length.
            mstore(add(m, 0x140), r)
            mstore(add(m, 0x160), s)
            mstore(add(m, 0x180), shl(248, v))
            if iszero(call(gas(), p, 0, add(m, 0x1c), 0x184, codesize(), 0x00)) {
                mstore(0x00, 0x6b836e6b) // `Permit2Failed()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

File 4 of 7 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

File 5 of 7 : IVotingEscrow.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.24;

interface IVotingEscrow {
    struct Point {
        int128 bias;
        int128 slope; // # -dweight / dt
        uint256 ts;
        uint256 blk; // block
    }

    struct LockedBalance {
        int128 amount;
        uint256 end;
    }

    function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external returns (uint256);

    function locked(uint256 id) external view returns (LockedBalance memory);
    function tokenOfOwnerByIndex(address _owner, uint256 _tokenIndex) external view returns (uint256);

    function token() external view returns (address);
    function team() external returns (address);
    function epoch() external view returns (uint256);
    function point_history(uint256 loc) external view returns (Point memory);
    function user_point_history(uint256 tokenId, uint256 loc) external view returns (Point memory);
    function user_point_epoch(uint256 tokenId) external view returns (uint256);

    function ownerOf(uint256) external view returns (address);
    function isApprovedOrOwner(address, uint256) external view returns (bool);
    function transferFrom(address, address, uint256) external;

    function isVotingApprovedOrOwner(address, uint256) external view returns (bool);
    function delegateVotingControl(address, uint256) external;

    function voted(uint256) external view returns (bool);
    function attachments(uint256) external view returns (uint256);
    function voting(uint256 tokenId) external;
    function abstain(uint256 tokenId) external;
    function attach(uint256 tokenId) external;
    function detach(uint256 tokenId) external;

    function checkpoint() external;
    function deposit_for(uint256 tokenId, uint256 value) external;

    function balanceOfNFT(uint256 _id) external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    function supply() external view returns (uint256);

    function decimals() external view returns (uint8);
}

File 6 of 7 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 7 of 7 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @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 Context {
    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;
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/solmate/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solmate/=lib/solmate/src/",
    "solady/=lib/solady/src/",
    "src/=src/",
    "test/=test/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_ve","type":"address"},{"internalType":"address","name":"_baseAsset","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CannotVoteWithNft","type":"error"},{"inputs":[],"name":"DepositFrozen","type":"error"},{"inputs":[],"name":"GaugeAlreadyKilled","type":"error"},{"inputs":[],"name":"GaugeAlreadyListed","type":"error"},{"inputs":[],"name":"GaugeNotKilled","type":"error"},{"inputs":[],"name":"GaugeNotListed","type":"error"},{"inputs":[],"name":"InvalidCap","type":"error"},{"inputs":[],"name":"InvalidParameter","type":"error"},{"inputs":[],"name":"KilledGauge","type":"error"},{"inputs":[],"name":"MaxArraySizeExceeded","type":"error"},{"inputs":[],"name":"NoVoteToRecast","type":"error"},{"inputs":[],"name":"NoVotingPower","type":"error"},{"inputs":[],"name":"NullAmount","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"VoteDelayNotExpired","type":"error"},{"inputs":[],"name":"VoteWeightOverflow","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"uint256","name":"period","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BudgetDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"DefaultCapUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"frozen","type":"bool"}],"name":"DepositFreezeTriggered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"GaugeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"GaugeCapUpdated","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":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldVoteDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newVoteDelay","type":"uint256"}],"name":"VoteDelayUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"}],"name":"VoteReseted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"uint256","name":"ts","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"weight","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votes","type":"uint256"}],"name":"Voted","type":"event"},{"inputs":[],"name":"PERIOD_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"string","name":"label","type":"string"},{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"addGauge","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"claimGaugeRewards","outputs":[{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositBudget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugeCaps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugeIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugeLabel","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugeStatus","outputs":[{"internalType":"bool","name":"isGauge","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"gaugeVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"gauges","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gaugesCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"gaugesDistributionTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"getGaugeCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"getGaugeRelativeWeight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"getGaugeRelativeWeightAtPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"getGaugeVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"getGaugeVotesAtPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getNftCurrentVotes","outputs":[{"components":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"votes","type":"uint256"}],"internalType":"struct Voter.CastedVote[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"getNftCurrentVotesAtPeriod","outputs":[{"components":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"votes","type":"uint256"}],"internalType":"struct Voter.CastedVote[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"gauge","type":"address"}],"name":"getNftVotesOnGauge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"getNftVotesOnGaugeAtPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ts","type":"uint256"}],"name":"getTotalVotesAtPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"isAlive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isDepositFrozen","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":"","type":"uint256"}],"name":"lastVoted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"periodBudget","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"recast","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"recastMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"reset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"resetMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"}],"name":"reviveGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalVotesPerPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"triggerDepositFreeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"uint256","name":"cap","type":"uint256"}],"name":"updateGaugeCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newVoteDelay","type":"uint256"}],"name":"updateVoteDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_defaultCap","type":"uint256"}],"name":"updatedefaultCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address[]","name":"gaugeList","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"vote","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"voteCastedPeriod","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voteDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address[]","name":"gaugeList","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"}],"name":"voteMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"votes","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"uint256","name":"votes","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"votesPerPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60c0346200015e57601f62003a9238819003918201601f19168301916001600160401b0383118484101762000162578084926060946040528339810103126200015e576200004d8162000176565b906200006a6040620000626020840162000176565b920162000176565b6001600160a01b039290919083168015620001465760018060a01b0319938460015416600155815f54958616175f5560405194167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3610e1060035567016345785d8a000060045560805260a05261390690816200018c82396080518181816103ba0152818161061d0152818161121801528181611496015281816117220152818161260301528181612a3d0152818161346d0152818161354101526135b1015260a0518181816109520152818161211301526122c10152f35b604051631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b03821682036200015e5756fe60406080815260049081361015610014575f80fd5b5f915f3560e01c80630604061814612c905780631703e5f914612c4457806317705c1214612ba95780631ac218b314612a615780631f85071614612a1157806324202739146128c857806325b6244014612861578063292fc979146128435780632951005c146127ef5780632c2a002e146127855780632f86075614612737578063310bd74b1461259957806334ce66e51461243a57806338e08f0e146122065780633e2f2e92146120a657806345464030146120325780634a0aa1a91461200157806351d7201414611f945780636558954f14611f7657806366ddf44714611eed57806367fec84d14611ec0578063689df56e14611ea35780636daa828014611a3b578063715018a6146119b4578063721af9c6146116c957806373464844146116615780637995a20e1461145557806379ba5097146113935780637ac09bf71461114f5780637c766f8f1461110b5780637f3725f3146110ec5780637f4222701461105157806386c2569e1461102a5780638da5cb5b14610ff7578063992a793314610f215780639965073414610ed45780639a0e7d6614610e965780639f06247b14610d71578063a3d6419214610d03578063a5a645c114610cdc578063a83038d014610c4b578063aa79979b14610c02578063b053918714610b9c578063c424616614610b58578063c8db7e7a14610aec578063cb1784df14610976578063cdf456e114610925578063e30c3978146108f0578063e6da0457146108a8578063ea02d53b14610881578063eb27f318146105ac578063f121da0a1461032c578063f2fde38b1461029a5763f3594be014610270575f80fd5b34610296576020600319360112610296576020928291358152600e845220549051908152f35b8280fd5b8334610329576020600319360112610329576102b4612cac565b6102bc6130b2565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b80fd5b509034610296576020806003193601126105a857823568929eee149b4bd21268933085541461059d57308555610361826131ac565b83517f16b7a011000000000000000000000000000000000000000000000000000000008152338183019081526020810184905273ffffffffffffffffffffffffffffffffffffffff9291908590829081906040010381867f0000000000000000000000000000000000000000000000000000000000000000165afa908115610593578891610566575b501561053f57828752600b8452848720610402612f50565b885284528487209085518083878295549384815201908b52878b20928b5b898282106105275750505061043792500383612e88565b8151908115610500575061044a81612f87565b9261045787519485612e88565b8184527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061048483612f87565b013687860137885b8281106104be5750505082916104b0916104a9600e9695336131f7565b8333613406565b855252429083205538905580f35b600190868b52600a8852888b206104d3612f50565b8c528852888b20836104e5838861301f565b51165f528852885f20546104f9828861301f565b520161048c565b86517fb06e56db000000000000000000000000000000000000000000000000000000008152fd5b85548916845260019586019588955093019201610420565b84517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6105869150853d871161058c575b61057e8183612e88565b810190613033565b5f6103ea565b503d610574565b86513d8a823e3d90fd5b63ab143c068652601cfd5b8380fd5b50346102965760606003193601126102965767ffffffffffffffff91813583811161087d576105de9036908401612edf565b92602435858111610879576105f69036908301612edf565b956044359081116108755761060e9036908401612edf565b969092600a871161084e5793957f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169490895b818110610661578a80f35b61066c81838b6130a2565b353068929eee149b4bd212685414610842573068929eee149b4bd2126855610693816131ac565b88517f16b7a011000000000000000000000000000000000000000000000000000000008152338582019081526020818101849052908290819060400103818c5afa908115610838578d91610819575b50156107f1578a85036107c9576106f981336131f7565b61070285612f87565b61070e8a519182612e88565b85815260059086821b8801602082013682116107ad5789905b8282106107b1575050508c61073b81612f87565b926107488d519485612e88565b81845260208401913690821b8c01116107ad578e918b905b83831b8d01821061079d5750505050829161077f916001959433613406565b8c52600e60205242898d20553868929eee149b4bd212685501610656565b8135815260209182019101610760565b8f80fd5b602080916107be84612cf2565b815201910190610727565b8389517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b8389517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b610832915060203d60201161058c5761057e8183612e88565b5f6106e2565b8a513d8f823e3d90fd5b8363ab143c068d52601cfd5b85517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b8780fd5b8680fd5b8480fd5b50346102965760206003193601126102965760209282913581526005845220549051908152f35b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff6108dc612cac565b1681526009845220549051908152f35b5080fd5b5050346108ec57816003193601126108ec5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b5050346108ec57816003193601126108ec576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b509190346108ec5761098736612ec9565b909362093a80809204828102928184041490151715610ac057508383526020600b81528284208285528152828420938351808684829854938481520190845284842092845b86828210610a94575050506109e392500386612e88565b8451926109ef84612f9f565b95825b858110610a0a57865180610a068a82612d13565b0390f35b8073ffffffffffffffffffffffffffffffffffffffff610a2c6001938561301f565b51168a8652600a87528886208587528752888620815f52875286895f208a51610a5481612e0f565b85825492838352015492839101528a5192610a6e84612e58565b83528883015289820152610a82828b61301f565b52610a8d818a61301f565b50016109f2565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958b9550930192016109cc565b8360116024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b508290346108ec5760606003193601126108ec576044359173ffffffffffffffffffffffffffffffffffffffff8316809303610b54578391358152600a602052818120602435825260205220905f52602052805f206001815491015482519182526020820152f35b5f80fd5b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff610b8c612cac565b1681526008845220549051908152f35b5034610296576020600319360112610296573591600254831015610329575073ffffffffffffffffffffffffffffffffffffffff60209260025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0154169051908152f35b5050346108ec5760206003193601126108ec5760ff8160209373ffffffffffffffffffffffffffffffffffffffff610c38612cac565b1681526010855220541690519015158152f35b50913461032957602060031936011261032957610c66612cac565b90610c6f612f50565b62093a808101809111610cb0576020945081849173ffffffffffffffffffffffffffffffffffffffff9352600c86522091165f528252805f20549051908152f35b6024826011877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5034610296576020600319360112610296576020928291358152600d845220549051908152f35b508290346108ec5760206003193601126108ec5762093a809081813504828102928184041490151715610d45576020848085858152600d845220549051908152f35b8260116024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b50346102965760206003193601126102965773ffffffffffffffffffffffffffffffffffffffff610da0612cac565b610da86130b2565b16918215610e705782845260106020528084209182549160ff831615610e4a5760ff8360081c16610e245750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790557fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa8280a280f35b517f9b4b8d1e000000000000000000000000000000000000000000000000000000008152fd5b517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b508290346108ec57816003193601126108ec57610eb1612f50565b9062093a808201809211610d45576020848085858152600d845220549051908152f35b50903461029657602060031936011261029657816020917f9b8d46d5ada9050fa0320d285f24811d709bba8d11887134fda797788740d0dc93358092610f186130b2565b5551908152a180f35b50346102965760206003193601126102965773ffffffffffffffffffffffffffffffffffffffff610f50612cac565b610f586130b2565b16918215610e705782845260106020528084209182549160ff831615610e4a5760ff8360081c1615610fd15750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690557f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba78280a280f35b517ffe236944000000000000000000000000000000000000000000000000000000008152fd5b5050346108ec57816003193601126108ec5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b5050346108ec57816003193601126108ec5760209060ff60015460a01c1690519015158152f35b5050346108ec57816003193601126108ec5760207f868eaac1d9e3d60a9df6592a6ac3e67619fd4ff575dc4cc9cba73fbf830cefb79161108f6130b2565b60ff6001547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000838360a01c161560a01b169116179182600155519160a01c1615158152a180f35b5050346108ec57816003193601126108ec576020906002549051908152f35b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff61113f612cac565b1681526006845220549051908152f35b5090346102965760606003193601126102965767ffffffffffffffff91803560243584811161138f576111859036908401612edf565b90946044359081116108795761119e9036908501612edf565b919068929eee149b4bd212689630885414611383573088556111bf856131ac565b86517f16b7a011000000000000000000000000000000000000000000000000000000008152338782019081526020818101889052979190889082908190604001038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115611379578b9161135c575b50156113355784840361130e575061126085336131f7565b61126983612f87565b9261127688519485612e88565b8084528684019060051b82019136831161130a578790915b8383106112f257505050506112a283612f87565b926112af87519485612e88565b8084528584019060051b8201913683116112ee57905b8282106112df5750505082916104b091600e959433613406565b813581529086019086016112c5565b8980fd5b81906112fd84612cf2565b815201910190879061128e565b8a80fd5b87517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b87517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6113739150883d8a1161058c5761057e8183612e88565b5f611248565b89513d8d823e3d90fd5b8563ab143c068a52601cfd5b8580fd5b5090346102965782600319360112610296576001549173ffffffffffffffffffffffffffffffffffffffff9133838516036114255750507fffffffffffffffffffffffff00000000000000000000000000000000000000008092166001555f549133908316175f553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b6024925051907f118cdaa70000000000000000000000000000000000000000000000000000000082523390820152fd5b5090346102965760209160206003193601126105a857803567ffffffffffffffff811161087d576114899036908301612edf565b9290600a841161163a57927f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690865b8181106114d9578780f35b6114e48183886130a2565b359068929eee149b4bd212683081541461162e57308155611504836131ac565b85517f16b7a01100000000000000000000000000000000000000000000000000000000815233888201908152602081018590528a90829081906040010381895afa908115611624578b91611607575b50156115df5761156383336131f7565b843b156112ee578986517fc1f0fb9f00000000000000000000000000000000000000000000000000000000815284898201528181602481838b5af180156115d5576115c1575b509260019352600e895242868b2055389055016114ce565b6115ca90612e74565b6112ee57895f6115a9565b88513d84823e3d90fd5b8686517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b61161e91508a3d8c1161058c5761057e8183612e88565b5f611553565b87513d8d823e3d90fd5b8663ab143c068b52601cfd5b50517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b50913461032957816003193601126103295761167b612cac565b9062093a808060243504818102918183041490151715610cb0576020945081849173ffffffffffffffffffffffffffffffffffffffff9352600c86522091165f528252805f20549051908152f35b509034610296576020806003193601126105a857823567ffffffffffffffff811161087d576116fb9036908501612edf565b929091600a93600a811161198c57929373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811694909190885b818110611754578980f35b61175f81838a6130a2565b3568929eee149b4bd2126890308254146119805730825561177f816131ac565b87517f16b7a01100000000000000000000000000000000000000000000000000000000815233818d019081526020810183905288908290819060400103818d5afa908115611976578d91611959575b501561193157808c52600b8752878c206117e6612f50565b8d528752878c208c8951809283918b825491828152019184528b842093905b8c8183106119155750505061181c92500382612e88565b805180156118ed5790816118308f93612f87565b9261183d8c519485612e88565b8184527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061186a83612f87565b01368c8601378b815b8a8d8585106118aa575050505050508291611896916104a96001979695336131f7565b8c52600e875242888d205538905501611749565b888352528181206001939291908e6118c0612f50565b835252208b6118cf838761301f565b51165f528c528c5f20546118e3828761301f565b52018f8c90611873565b8c8a517fb06e56db000000000000000000000000000000000000000000000000000000008152fd5b60019294508291938d8754168152019401920184929391611805565b8a88517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6119709150883d8a1161058c5761057e8183612e88565b5f6117ce565b89513d8f823e3d90fd5b8a63ab143c068d52601cfd5b8583517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b83346103295780600319360112610329576119cd6130b2565b5f73ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000806001541660015582549081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50903461029657606060031936011261029657611a56612cac565b90602480359267ffffffffffffffff958685116103295736602386011215610329578486013595878711611e78576020977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe097865197611abd8b8b601f850116018a612e88565b8189523687838301011161087d57818592888d9301838c01378901015273ffffffffffffffffffffffffffffffffffffffff60443594611afb6130b2565b16948515611e505785845260108a52868420805460ff8116611e2857670de0b6b3a76400008711611e00577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166101011790556002549768010000000000000000891015611dd55760019160018a01806002558a1015611dab5760025f52897f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01887fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905587865260068c52898987205560078c52888620948251948511611d8157505090839291611bf48c97969554612dbe565b601f8111611d48575b508691601f8411600114611ca457507f0fab41fe8607aed4f537e3a13c17a440a2ba7eb1af6dca1c1776eae2d735aee99798999a509082918592611c99575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790555b84815260088352818682205585611c83612f50565b918681526009855220558451908152a251908152f35b015190505f80611c3c565b929a8b168486528786209386905b828210611d3157505091600193918c7f0fab41fe8607aed4f537e3a13c17a440a2ba7eb1af6dca1c1776eae2d735aee99a9b9c9d9410611cfa575b505050811b019055611c6e565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080611ced565b838501518655948501949389019390890190611cb2565b611d7190858752888720601f860160051c8101918a8710611d77575b601f0160051c019061308c565b5f611bfd565b9091508190611d64565b604187917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6032857f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b506041837f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b8489517fcadd5a68000000000000000000000000000000000000000000000000000000008152fd5b8489517fe3ce9c7f000000000000000000000000000000000000000000000000000000008152fd5b8287517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8192507f4e487b71000000000000000000000000000000000000000000000000000000006041925252fd5b503461029657826003193601126102965760209250549051908152f35b5050346108ec5760206003193601126108ec57602090611ee6611ee1612cac565b613058565b9051908152f35b503461029657602060031936011261029657803590611f0a6130b2565b62093a80821015611f4f5750907f40ed3b0f84511a79fc28fe2eb99ba62a4ebf0264822e23a5d2f4ffdf0261164b91600354908060035582519182526020820152a180f35b82517f613970e0000000000000000000000000000000000000000000000000000000008152fd5b5050346108ec57816003193601126108ec576020905162093a808152f35b509134610329578160031936011261032957611fae612cac565b62093a80918260243504838102938185041490151715611fd557602084611ee68585613102565b806011867f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b5050346108ec5760ff8160209361201736612ec9565b908252600f8652828220908252855220541690519015158152f35b5091346103295760606003193601126103295761204d612ccf565b9062093a808060443504818102918183041490151715610cb05773ffffffffffffffffffffffffffffffffffffffff918491602096358252600a875282822090825286522091165f5282526001815f2001549051908152f35b50903461029657602060031936011261029657813568929eee149b4bd2126892308454146121fb5730845581156121d45760ff60015460a01c166121ad57825190826060523084523360601b602c526f23b872dd000000000000000000000000600c526020866064601c827f00000000000000000000000000000000000000000000000000000000000000005af13d1560018851141716156121a25750846060528252612159612154612f50565b612f6a565b918285526005602052808520612170838254612f7a565b9055519081527f7bb731c94eef65c5886be4b79a2654869ac33cd509e9d806fa6dce7873ba4a5660203392a338905580f35b637939f4248652601cfd5b82517f02dd0225000000000000000000000000000000000000000000000000000000008152fd5b82517fe5a74490000000000000000000000000000000000000000000000000000000008152fd5b63ab143c068552601cfd5b509034610296576020928360031936011261032957612223612cac565b68929eee149b4bd21268913083541461242e573083558094612243612f50565b9273ffffffffffffffffffffffffffffffffffffffff81169384845260098952868420545b898282111561235c57505062093a8081018091116123305784845260098952868420558661229e575b5050505038905551908152f35b601452856034526fa9059cbb0000000000000000000000008252868260446010827f00000000000000000000000000000000000000000000000000000000000000005af13d15600184511417161561232557506034527f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f7241858451868152a25f808080612291565b6390b8ec188252601cfd5b6024846011857f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b98670de0b6b3a764000061239f878b859d6123a69661237a8a613058565b612384828c613102565b938185116123e1575b50506005929352528b89205490612f10565b0490612f7a565b9762093a80810180911115612268576024856011867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b612423906123f18360059761304b565b92968481528961240988958689528484205490612f10565b0493612416612154612f50565b8252865220918254612f7a565b90558b93928f61238d565b63ab143c06859152601cfd5b5050346108ec57602091826003193601126103295773ffffffffffffffffffffffffffffffffffffffff61246c612cac565b168152600783528181209083835180938390805461248981612dbe565b80855291600191808316908115612557575060011461251a575b5050506124b69250959392950382612e88565b82519382859384528251928382860152825b84811061250457505050828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b81810183015188820188015287955082016124c8565b86528486209492508591905b81831061253f5750889450508201016124b65f806124a3565b85548884018501529485019487945091830191612526565b9150506124b69593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b82010186925f806124a3565b509034610b54576020600319360112610b5457813568929eee149b4bd21268923084541461272c573084556125cd826131ac565b82517f16b7a0110000000000000000000000000000000000000000000000000000000081523381830190815260208181018590527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16939291829081906040010381865afa908115612722575f91612703575b50156126dc5761266583336131f7565b813b15610b54578260245f9283875195869485937fc1f0fb9f0000000000000000000000000000000000000000000000000000000085528401525af180156126d2576126bf575b508352600e602052429083205538905580f35b6126ca919450612e74565b5f925f6126ac565b83513d5f823e3d90fd5b83517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b61271c915060203d60201161058c5761057e8183612e88565b5f612655565b85513d5f823e3d90fd5b63ab143c065f52601cfd5b509034610b545780600319360112610b5457602091612754612ccf565b90355f52600c835273ffffffffffffffffffffffffffffffffffffffff825f2091165f528252805f20549051908152f35b509034610b54576020600319360112610b54576127a0612cac565b6127a8612f50565b9262093a8084018094116127c35750602092611ee691613102565b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610b54576020600319360112610b545773ffffffffffffffffffffffffffffffffffffffff61281e612cac565b165f526010602052805f205460ff8251918181161515835260081c1615156020820152f35b8234610b54575f600319360112610b54576020906003549051908152f35b509034610b54576060600319360112610b545760443591355f52600b602052805f206024355f52602052805f20918254811015610b54576128b973ffffffffffffffffffffffffffffffffffffffff91602094612d7c565b92905490519260031b1c168152f35b509034610b545780600319360112610b54576128e2612cac565b9073ffffffffffffffffffffffffffffffffffffffff602435926129046130b2565b169283156129ea57835f526010602052815f205460ff8116156129c25760081c60ff161561299b57670de0b6b3a7640000831161297457507fc039168563328bf91df25baf90beebb542996a663fa0b7865526a2106b0567b391602091845f526008835281815f205551908152a2005b90517fcadd5a68000000000000000000000000000000000000000000000000000000008152fd5b90517f6cd7ad06000000000000000000000000000000000000000000000000000000008152fd5b5090517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b90517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8234610b54575f600319360112610b54576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b509034610b545760209081600319360112610b5457823592612a81612f50565b9062093a8082018092116127c35750835f52600b8352815f20815f528352815f209282518085838297549384815201905f52835f20925f5b85828210612b7d57505050612ad092500385612e88565b835191612adc83612f9f565b945f5b848110612af357855180610a068982612d13565b8073ffffffffffffffffffffffffffffffffffffffff612b156001938561301f565b5116895f52600a8652875f20855f528652875f20815f52865285885f208951612b3d81612e0f565b8582549283835201549283910152895192612b5784612e58565b83528783015288820152612b6b828a61301f565b52612b76818961301f565b5001612adf565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958a955093019201612ab9565b509034610b545780600319360112610b5457612bc3612ccf565b82355f52600a602052815f20612bd7612f50565b62093a808101809111612c1857602094505f52835273ffffffffffffffffffffffffffffffffffffffff825f2091165f5282526001815f2001549051908152f35b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610b54576020600319360112610b545760209073ffffffffffffffffffffffffffffffffffffffff612c76612cac565b165f526010825260ff815f205460081c1690519015158152f35b8234610b54575f600319360112610b5457602090611ee6612f50565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b6024359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b60208082019080835283518092528060408094019401925f905b838210612d3c57505050505090565b8451805173ffffffffffffffffffffffffffffffffffffffff16875280840151878501528101518682015260609095019493820193600190910190612d2d565b8054821015612d91575f5260205f2001905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90600182811c92168015612e05575b6020831014612dd857565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691612dcd565b6040810190811067ffffffffffffffff821117612e2b57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117612e2b57604052565b67ffffffffffffffff8111612e2b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612e2b57604052565b6003196040910112610b54576004359060243590565b9181601f84011215610b545782359167ffffffffffffffff8311610b54576020808501948460051b010111610b5457565b81810292918115918404141715612f2357565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b62093a80804204818102918183041490151715612f235790565b90621275008201809211612f2357565b91908201809211612f2357565b67ffffffffffffffff8111612e2b5760051b60200190565b90612fa982612f87565b604090612fb96040519182612e88565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0612fe78295612f87565b01915f5b838110612ff85750505050565b602090825161300681612e58565b5f8152825f818301525f85830152828601015201612feb565b8051821015612d915760209160051b010190565b90816020910312610b5457518015158103610b545790565b91908203918211612f2357565b73ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f205480155f14613089575060045490565b90565b818110613097575050565b5f815560010161308c565b9190811015612d915760051b0190565b73ffffffffffffffffffffffffffffffffffffffff5f541633036130d257565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b73ffffffffffffffffffffffffffffffffffffffff1690815f52601060205260405f206040519061313282612e0f565b549060ff82161560ff81159384845260081c16159160208315910152916131a4575b5061319e575f52600d60205260405f205490811561319e57600c60205260405f20905f5260205260405f2054670de0b6b3a764000090818102918183041490151715612f23570490565b50505f90565b90505f613154565b5f52600e6020526131c460405f205460035490612f7a565b4211156131cd57565b60046040517fb7e57455000000000000000000000000000000000000000000000000000000008152fd5b90613200612f50565b9062093a808201809211612f2357805f52602090600f8252604093845f20845f52835260ff855f205416613267575b505f52600f8152825f20915f52525f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b815f52600b8352845f20845f52835282855f20918651809384918482549182815201915f52845f20905f5b8181106133d757505050906132a8910384612e88565b8251905f935f5b88878b8684106133155750955050505050505f52600b8352845f20845f528352845f2080545f8255806132fc575b5050835f52600d83526132f4855f2091825461304b565b90555f61322f565b61330e915f52845f209081019061308c565b5f806132dd565b5f6001959697989973ffffffffffffffffffffffffffffffffffffffff928361333e888b61301f565b511695858452600a9c8d81528285208286528152828520888652815261338a8a9485858820015490848852600c84528588208b8952845285882061338383825461304b565b9055612f7a565b8786529d8152828520918552528083208684528e528220828155015586167f8e9be633930d447edb768a7d9ee161eb92b33522fcca02370272cc378b720ddf5f80a40190869392916132af565b825473ffffffffffffffffffffffffffffffffffffffff16845289968895509093019260019283019201613292565b919290613411612f50565b915f9362093a8084018411612f2357855190604051927fe7e242d400000000000000000000000000000000000000000000000000000000845284600485015260208460248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9384156135fd575f946138c5575b50831561389b575f97889485949392915b8486106136325750505050505061271081116136085761352a575b60409362093a8083018452600d6020526134e7858520918254612f7a565b90558252600f60205262093a80838320910182526020522060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b15610b5457604051937ffd4a77f10000000000000000000000000000000000000000000000000000000085528160048601525f856024818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19485156135fd576040956135ea575b5093506134c9565b6135f5919450612e74565b5f925f6135e2565b6040513d5f823e3d90fd5b60046040517fb658683c000000000000000000000000000000000000000000000000000000008152fd5b9091929394999573ffffffffffffffffffffffffffffffffffffffff6136588c8461301f565b51169a8b15613871578b5f52601060205260405f206040519061367a82612e0f565b549060ff6020818416159283158152019260081c161515825261384757511561381d576136d66136d0926127106136bb6136b4858a61301f565b5188612f10565b049384916136c9858a61301f565b5190612f7a565b99612f7a565b9b895f52600b60205260405f2062093a808c015f5260205260405f2080549368010000000000000000851015612e2b578c8c9261371b60019788928382018155612d7c565b81549060031b9073ffffffffffffffffffffffffffffffffffffffff88831b921b191617905562093a8082015f52600c60205260405f20855f5260205260405f20613767848254612f7a565b9055613773868b61301f565b51916040519261378284612e0f565b83526020830190848252855f52600a60205262093a8060405f2091015f5260205260405f20865f5260205260405f2092518355519101556137c3848961301f565b519060405191428352602083015260408201527f2e6c94de62679b33b86aa0615e0826812095a06925ae30e3fa2234858f0ca986606073ffffffffffffffffffffffffffffffffffffffff8b1692a40194939291906134ae565b60046040517f6cd7ad06000000000000000000000000000000000000000000000000000000008152fd5b60046040517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f7c176b74000000000000000000000000000000000000000000000000000000008152fd5b9093506020813d6020116138f1575b816138e160209383612e88565b81010312610b545751925f61349d565b3d91506138d456fea164736f6c6343000818000a00000000000000000000000010b34b53b5989aa893c18e7fad4c460f98ce3847000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba

Deployed Bytecode

0x60406080815260049081361015610014575f80fd5b5f915f3560e01c80630604061814612c905780631703e5f914612c4457806317705c1214612ba95780631ac218b314612a615780631f85071614612a1157806324202739146128c857806325b6244014612861578063292fc979146128435780632951005c146127ef5780632c2a002e146127855780632f86075614612737578063310bd74b1461259957806334ce66e51461243a57806338e08f0e146122065780633e2f2e92146120a657806345464030146120325780634a0aa1a91461200157806351d7201414611f945780636558954f14611f7657806366ddf44714611eed57806367fec84d14611ec0578063689df56e14611ea35780636daa828014611a3b578063715018a6146119b4578063721af9c6146116c957806373464844146116615780637995a20e1461145557806379ba5097146113935780637ac09bf71461114f5780637c766f8f1461110b5780637f3725f3146110ec5780637f4222701461105157806386c2569e1461102a5780638da5cb5b14610ff7578063992a793314610f215780639965073414610ed45780639a0e7d6614610e965780639f06247b14610d71578063a3d6419214610d03578063a5a645c114610cdc578063a83038d014610c4b578063aa79979b14610c02578063b053918714610b9c578063c424616614610b58578063c8db7e7a14610aec578063cb1784df14610976578063cdf456e114610925578063e30c3978146108f0578063e6da0457146108a8578063ea02d53b14610881578063eb27f318146105ac578063f121da0a1461032c578063f2fde38b1461029a5763f3594be014610270575f80fd5b34610296576020600319360112610296576020928291358152600e845220549051908152f35b8280fd5b8334610329576020600319360112610329576102b4612cac565b6102bc6130b2565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558254167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b80fd5b509034610296576020806003193601126105a857823568929eee149b4bd21268933085541461059d57308555610361826131ac565b83517f16b7a011000000000000000000000000000000000000000000000000000000008152338183019081526020810184905273ffffffffffffffffffffffffffffffffffffffff9291908590829081906040010381867f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb165afa908115610593578891610566575b501561053f57828752600b8452848720610402612f50565b885284528487209085518083878295549384815201908b52878b20928b5b898282106105275750505061043792500383612e88565b8151908115610500575061044a81612f87565b9261045787519485612e88565b8184527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061048483612f87565b013687860137885b8281106104be5750505082916104b0916104a9600e9695336131f7565b8333613406565b855252429083205538905580f35b600190868b52600a8852888b206104d3612f50565b8c528852888b20836104e5838861301f565b51165f528852885f20546104f9828861301f565b520161048c565b86517fb06e56db000000000000000000000000000000000000000000000000000000008152fd5b85548916845260019586019588955093019201610420565b84517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6105869150853d871161058c575b61057e8183612e88565b810190613033565b5f6103ea565b503d610574565b86513d8a823e3d90fd5b63ab143c068652601cfd5b8380fd5b50346102965760606003193601126102965767ffffffffffffffff91813583811161087d576105de9036908401612edf565b92602435858111610879576105f69036908301612edf565b956044359081116108755761060e9036908401612edf565b969092600a871161084e5793957f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb73ffffffffffffffffffffffffffffffffffffffff169490895b818110610661578a80f35b61066c81838b6130a2565b353068929eee149b4bd212685414610842573068929eee149b4bd2126855610693816131ac565b88517f16b7a011000000000000000000000000000000000000000000000000000000008152338582019081526020818101849052908290819060400103818c5afa908115610838578d91610819575b50156107f1578a85036107c9576106f981336131f7565b61070285612f87565b61070e8a519182612e88565b85815260059086821b8801602082013682116107ad5789905b8282106107b1575050508c61073b81612f87565b926107488d519485612e88565b81845260208401913690821b8c01116107ad578e918b905b83831b8d01821061079d5750505050829161077f916001959433613406565b8c52600e60205242898d20553868929eee149b4bd212685501610656565b8135815260209182019101610760565b8f80fd5b602080916107be84612cf2565b815201910190610727565b8389517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b8389517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b610832915060203d60201161058c5761057e8183612e88565b5f6106e2565b8a513d8f823e3d90fd5b8363ab143c068d52601cfd5b85517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b8780fd5b8680fd5b8480fd5b50346102965760206003193601126102965760209282913581526005845220549051908152f35b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff6108dc612cac565b1681526009845220549051908152f35b5080fd5b5050346108ec57816003193601126108ec5760209073ffffffffffffffffffffffffffffffffffffffff600154169051908152f35b5050346108ec57816003193601126108ec576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba168152f35b509190346108ec5761098736612ec9565b909362093a80809204828102928184041490151715610ac057508383526020600b81528284208285528152828420938351808684829854938481520190845284842092845b86828210610a94575050506109e392500386612e88565b8451926109ef84612f9f565b95825b858110610a0a57865180610a068a82612d13565b0390f35b8073ffffffffffffffffffffffffffffffffffffffff610a2c6001938561301f565b51168a8652600a87528886208587528752888620815f52875286895f208a51610a5481612e0f565b85825492838352015492839101528a5192610a6e84612e58565b83528883015289820152610a82828b61301f565b52610a8d818a61301f565b50016109f2565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958b9550930192016109cc565b8360116024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b508290346108ec5760606003193601126108ec576044359173ffffffffffffffffffffffffffffffffffffffff8316809303610b54578391358152600a602052818120602435825260205220905f52602052805f206001815491015482519182526020820152f35b5f80fd5b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff610b8c612cac565b1681526008845220549051908152f35b5034610296576020600319360112610296573591600254831015610329575073ffffffffffffffffffffffffffffffffffffffff60209260025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace0154169051908152f35b5050346108ec5760206003193601126108ec5760ff8160209373ffffffffffffffffffffffffffffffffffffffff610c38612cac565b1681526010855220541690519015158152f35b50913461032957602060031936011261032957610c66612cac565b90610c6f612f50565b62093a808101809111610cb0576020945081849173ffffffffffffffffffffffffffffffffffffffff9352600c86522091165f528252805f20549051908152f35b6024826011877f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5034610296576020600319360112610296576020928291358152600d845220549051908152f35b508290346108ec5760206003193601126108ec5762093a809081813504828102928184041490151715610d45576020848085858152600d845220549051908152f35b8260116024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b50346102965760206003193601126102965773ffffffffffffffffffffffffffffffffffffffff610da0612cac565b610da86130b2565b16918215610e705782845260106020528084209182549160ff831615610e4a5760ff8360081c16610e245750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790557fed18e9faa3dccfd8aa45f69c4de40546b2ca9cccc4538a2323531656516db1aa8280a280f35b517f9b4b8d1e000000000000000000000000000000000000000000000000000000008152fd5b517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b508290346108ec57816003193601126108ec57610eb1612f50565b9062093a808201809211610d45576020848085858152600d845220549051908152f35b50903461029657602060031936011261029657816020917f9b8d46d5ada9050fa0320d285f24811d709bba8d11887134fda797788740d0dc93358092610f186130b2565b5551908152a180f35b50346102965760206003193601126102965773ffffffffffffffffffffffffffffffffffffffff610f50612cac565b610f586130b2565b16918215610e705782845260106020528084209182549160ff831615610e4a5760ff8360081c1615610fd15750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1690557f04a5d3f5d80d22d9345acc80618f4a4e7e663cf9e1aed23b57d975acec002ba78280a280f35b517ffe236944000000000000000000000000000000000000000000000000000000008152fd5b5050346108ec57816003193601126108ec5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b5050346108ec57816003193601126108ec5760209060ff60015460a01c1690519015158152f35b5050346108ec57816003193601126108ec5760207f868eaac1d9e3d60a9df6592a6ac3e67619fd4ff575dc4cc9cba73fbf830cefb79161108f6130b2565b60ff6001547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000838360a01c161560a01b169116179182600155519160a01c1615158152a180f35b5050346108ec57816003193601126108ec576020906002549051908152f35b5050346108ec5760206003193601126108ec578060209273ffffffffffffffffffffffffffffffffffffffff61113f612cac565b1681526006845220549051908152f35b5090346102965760606003193601126102965767ffffffffffffffff91803560243584811161138f576111859036908401612edf565b90946044359081116108795761119e9036908501612edf565b919068929eee149b4bd212689630885414611383573088556111bf856131ac565b86517f16b7a011000000000000000000000000000000000000000000000000000000008152338782019081526020818101889052979190889082908190604001038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb165afa908115611379578b9161135c575b50156113355784840361130e575061126085336131f7565b61126983612f87565b9261127688519485612e88565b8084528684019060051b82019136831161130a578790915b8383106112f257505050506112a283612f87565b926112af87519485612e88565b8084528584019060051b8201913683116112ee57905b8282106112df5750505082916104b091600e959433613406565b813581529086019086016112c5565b8980fd5b81906112fd84612cf2565b815201910190879061128e565b8a80fd5b87517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b87517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6113739150883d8a1161058c5761057e8183612e88565b5f611248565b89513d8d823e3d90fd5b8563ab143c068a52601cfd5b8580fd5b5090346102965782600319360112610296576001549173ffffffffffffffffffffffffffffffffffffffff9133838516036114255750507fffffffffffffffffffffffff00000000000000000000000000000000000000008092166001555f549133908316175f553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a380f35b6024925051907f118cdaa70000000000000000000000000000000000000000000000000000000082523390820152fd5b5090346102965760209160206003193601126105a857803567ffffffffffffffff811161087d576114899036908301612edf565b9290600a841161163a57927f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb73ffffffffffffffffffffffffffffffffffffffff1690865b8181106114d9578780f35b6114e48183886130a2565b359068929eee149b4bd212683081541461162e57308155611504836131ac565b85517f16b7a01100000000000000000000000000000000000000000000000000000000815233888201908152602081018590528a90829081906040010381895afa908115611624578b91611607575b50156115df5761156383336131f7565b843b156112ee578986517fc1f0fb9f00000000000000000000000000000000000000000000000000000000815284898201528181602481838b5af180156115d5576115c1575b509260019352600e895242868b2055389055016114ce565b6115ca90612e74565b6112ee57895f6115a9565b88513d84823e3d90fd5b8686517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b61161e91508a3d8c1161058c5761057e8183612e88565b5f611553565b87513d8d823e3d90fd5b8663ab143c068b52601cfd5b50517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b50913461032957816003193601126103295761167b612cac565b9062093a808060243504818102918183041490151715610cb0576020945081849173ffffffffffffffffffffffffffffffffffffffff9352600c86522091165f528252805f20549051908152f35b509034610296576020806003193601126105a857823567ffffffffffffffff811161087d576116fb9036908501612edf565b929091600a93600a811161198c57929373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb811694909190885b818110611754578980f35b61175f81838a6130a2565b3568929eee149b4bd2126890308254146119805730825561177f816131ac565b87517f16b7a01100000000000000000000000000000000000000000000000000000000815233818d019081526020810183905288908290819060400103818d5afa908115611976578d91611959575b501561193157808c52600b8752878c206117e6612f50565b8d528752878c208c8951809283918b825491828152019184528b842093905b8c8183106119155750505061181c92500382612e88565b805180156118ed5790816118308f93612f87565b9261183d8c519485612e88565b8184527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061186a83612f87565b01368c8601378b815b8a8d8585106118aa575050505050508291611896916104a96001979695336131f7565b8c52600e875242888d205538905501611749565b888352528181206001939291908e6118c0612f50565b835252208b6118cf838761301f565b51165f528c528c5f20546118e3828761301f565b52018f8c90611873565b8c8a517fb06e56db000000000000000000000000000000000000000000000000000000008152fd5b60019294508291938d8754168152019401920184929391611805565b8a88517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b6119709150883d8a1161058c5761057e8183612e88565b5f6117ce565b89513d8f823e3d90fd5b8a63ab143c068d52601cfd5b8583517f1d30d2f8000000000000000000000000000000000000000000000000000000008152fd5b83346103295780600319360112610329576119cd6130b2565b5f73ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000806001541660015582549081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b50903461029657606060031936011261029657611a56612cac565b90602480359267ffffffffffffffff958685116103295736602386011215610329578486013595878711611e78576020977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe097865197611abd8b8b601f850116018a612e88565b8189523687838301011161087d57818592888d9301838c01378901015273ffffffffffffffffffffffffffffffffffffffff60443594611afb6130b2565b16948515611e505785845260108a52868420805460ff8116611e2857670de0b6b3a76400008711611e00577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000166101011790556002549768010000000000000000891015611dd55760019160018a01806002558a1015611dab5760025f52897f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace01887fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905587865260068c52898987205560078c52888620948251948511611d8157505090839291611bf48c97969554612dbe565b601f8111611d48575b508691601f8411600114611ca457507f0fab41fe8607aed4f537e3a13c17a440a2ba7eb1af6dca1c1776eae2d735aee99798999a509082918592611c99575b50507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8260011b9260031b1c19161790555b84815260088352818682205585611c83612f50565b918681526009855220558451908152a251908152f35b015190505f80611c3c565b929a8b168486528786209386905b828210611d3157505091600193918c7f0fab41fe8607aed4f537e3a13c17a440a2ba7eb1af6dca1c1776eae2d735aee99a9b9c9d9410611cfa575b505050811b019055611c6e565b01517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88460031b161c191690555f8080611ced565b838501518655948501949389019390890190611cb2565b611d7190858752888720601f860160051c8101918a8710611d77575b601f0160051c019061308c565b5f611bfd565b9091508190611d64565b604187917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6032857f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b506041837f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b8489517fcadd5a68000000000000000000000000000000000000000000000000000000008152fd5b8489517fe3ce9c7f000000000000000000000000000000000000000000000000000000008152fd5b8287517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8192507f4e487b71000000000000000000000000000000000000000000000000000000006041925252fd5b503461029657826003193601126102965760209250549051908152f35b5050346108ec5760206003193601126108ec57602090611ee6611ee1612cac565b613058565b9051908152f35b503461029657602060031936011261029657803590611f0a6130b2565b62093a80821015611f4f5750907f40ed3b0f84511a79fc28fe2eb99ba62a4ebf0264822e23a5d2f4ffdf0261164b91600354908060035582519182526020820152a180f35b82517f613970e0000000000000000000000000000000000000000000000000000000008152fd5b5050346108ec57816003193601126108ec576020905162093a808152f35b509134610329578160031936011261032957611fae612cac565b62093a80918260243504838102938185041490151715611fd557602084611ee68585613102565b806011867f4e487b71000000000000000000000000000000000000000000000000000000006024945252fd5b5050346108ec5760ff8160209361201736612ec9565b908252600f8652828220908252855220541690519015158152f35b5091346103295760606003193601126103295761204d612ccf565b9062093a808060443504818102918183041490151715610cb05773ffffffffffffffffffffffffffffffffffffffff918491602096358252600a875282822090825286522091165f5282526001815f2001549051908152f35b50903461029657602060031936011261029657813568929eee149b4bd2126892308454146121fb5730845581156121d45760ff60015460a01c166121ad57825190826060523084523360601b602c526f23b872dd000000000000000000000000600c526020866064601c827f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba5af13d1560018851141716156121a25750846060528252612159612154612f50565b612f6a565b918285526005602052808520612170838254612f7a565b9055519081527f7bb731c94eef65c5886be4b79a2654869ac33cd509e9d806fa6dce7873ba4a5660203392a338905580f35b637939f4248652601cfd5b82517f02dd0225000000000000000000000000000000000000000000000000000000008152fd5b82517fe5a74490000000000000000000000000000000000000000000000000000000008152fd5b63ab143c068552601cfd5b509034610296576020928360031936011261032957612223612cac565b68929eee149b4bd21268913083541461242e573083558094612243612f50565b9273ffffffffffffffffffffffffffffffffffffffff81169384845260098952868420545b898282111561235c57505062093a8081018091116123305784845260098952868420558661229e575b5050505038905551908152f35b601452856034526fa9059cbb0000000000000000000000008252868260446010827f0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba5af13d15600184511417161561232557506034527f106f923f993c2149d49b4255ff723acafa1f2d94393f561d3eda32ae348f7241858451868152a25f808080612291565b6390b8ec188252601cfd5b6024846011857f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b98670de0b6b3a764000061239f878b859d6123a69661237a8a613058565b612384828c613102565b938185116123e1575b50506005929352528b89205490612f10565b0490612f7a565b9762093a80810180911115612268576024856011867f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b612423906123f18360059761304b565b92968481528961240988958689528484205490612f10565b0493612416612154612f50565b8252865220918254612f7a565b90558b93928f61238d565b63ab143c06859152601cfd5b5050346108ec57602091826003193601126103295773ffffffffffffffffffffffffffffffffffffffff61246c612cac565b168152600783528181209083835180938390805461248981612dbe565b80855291600191808316908115612557575060011461251a575b5050506124b69250959392950382612e88565b82519382859384528251928382860152825b84811061250457505050828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b81810183015188820188015287955082016124c8565b86528486209492508591905b81831061253f5750889450508201016124b65f806124a3565b85548884018501529485019487945091830191612526565b9150506124b69593507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b82010186925f806124a3565b509034610b54576020600319360112610b5457813568929eee149b4bd21268923084541461272c573084556125cd826131ac565b82517f16b7a0110000000000000000000000000000000000000000000000000000000081523381830190815260208181018590527f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb73ffffffffffffffffffffffffffffffffffffffff16939291829081906040010381865afa908115612722575f91612703575b50156126dc5761266583336131f7565b813b15610b54578260245f9283875195869485937fc1f0fb9f0000000000000000000000000000000000000000000000000000000085528401525af180156126d2576126bf575b508352600e602052429083205538905580f35b6126ca919450612e74565b5f925f6126ac565b83513d5f823e3d90fd5b83517f33b78c8e000000000000000000000000000000000000000000000000000000008152fd5b61271c915060203d60201161058c5761057e8183612e88565b5f612655565b85513d5f823e3d90fd5b63ab143c065f52601cfd5b509034610b545780600319360112610b5457602091612754612ccf565b90355f52600c835273ffffffffffffffffffffffffffffffffffffffff825f2091165f528252805f20549051908152f35b509034610b54576020600319360112610b54576127a0612cac565b6127a8612f50565b9262093a8084018094116127c35750602092611ee691613102565b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610b54576020600319360112610b545773ffffffffffffffffffffffffffffffffffffffff61281e612cac565b165f526010602052805f205460ff8251918181161515835260081c1615156020820152f35b8234610b54575f600319360112610b54576020906003549051908152f35b509034610b54576060600319360112610b545760443591355f52600b602052805f206024355f52602052805f20918254811015610b54576128b973ffffffffffffffffffffffffffffffffffffffff91602094612d7c565b92905490519260031b1c168152f35b509034610b545780600319360112610b54576128e2612cac565b9073ffffffffffffffffffffffffffffffffffffffff602435926129046130b2565b169283156129ea57835f526010602052815f205460ff8116156129c25760081c60ff161561299b57670de0b6b3a7640000831161297457507fc039168563328bf91df25baf90beebb542996a663fa0b7865526a2106b0567b391602091845f526008835281815f205551908152a2005b90517fcadd5a68000000000000000000000000000000000000000000000000000000008152fd5b90517f6cd7ad06000000000000000000000000000000000000000000000000000000008152fd5b5090517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b90517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8234610b54575f600319360112610b54576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb168152f35b509034610b545760209081600319360112610b5457823592612a81612f50565b9062093a8082018092116127c35750835f52600b8352815f20815f528352815f209282518085838297549384815201905f52835f20925f5b85828210612b7d57505050612ad092500385612e88565b835191612adc83612f9f565b945f5b848110612af357855180610a068982612d13565b8073ffffffffffffffffffffffffffffffffffffffff612b156001938561301f565b5116895f52600a8652875f20855f528652875f20815f52865285885f208951612b3d81612e0f565b8582549283835201549283910152895192612b5784612e58565b83528783015288820152612b6b828a61301f565b52612b76818961301f565b5001612adf565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958a955093019201612ab9565b509034610b545780600319360112610b5457612bc3612ccf565b82355f52600a602052815f20612bd7612f50565b62093a808101809111612c1857602094505f52835273ffffffffffffffffffffffffffffffffffffffff825f2091165f5282526001815f2001549051908152f35b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b8234610b54576020600319360112610b545760209073ffffffffffffffffffffffffffffffffffffffff612c76612cac565b165f526010825260ff815f205460081c1690519015158152f35b8234610b54575f600319360112610b5457602090611ee6612f50565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b6024359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b359073ffffffffffffffffffffffffffffffffffffffff82168203610b5457565b60208082019080835283518092528060408094019401925f905b838210612d3c57505050505090565b8451805173ffffffffffffffffffffffffffffffffffffffff16875280840151878501528101518682015260609095019493820193600190910190612d2d565b8054821015612d91575f5260205f2001905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b90600182811c92168015612e05575b6020831014612dd857565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691612dcd565b6040810190811067ffffffffffffffff821117612e2b57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117612e2b57604052565b67ffffffffffffffff8111612e2b57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612e2b57604052565b6003196040910112610b54576004359060243590565b9181601f84011215610b545782359167ffffffffffffffff8311610b54576020808501948460051b010111610b5457565b81810292918115918404141715612f2357565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b62093a80804204818102918183041490151715612f235790565b90621275008201809211612f2357565b91908201809211612f2357565b67ffffffffffffffff8111612e2b5760051b60200190565b90612fa982612f87565b604090612fb96040519182612e88565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0612fe78295612f87565b01915f5b838110612ff85750505050565b602090825161300681612e58565b5f8152825f818301525f85830152828601015201612feb565b8051821015612d915760209160051b010190565b90816020910312610b5457518015158103610b545790565b91908203918211612f2357565b73ffffffffffffffffffffffffffffffffffffffff165f52600860205260405f205480155f14613089575060045490565b90565b818110613097575050565b5f815560010161308c565b9190811015612d915760051b0190565b73ffffffffffffffffffffffffffffffffffffffff5f541633036130d257565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b73ffffffffffffffffffffffffffffffffffffffff1690815f52601060205260405f206040519061313282612e0f565b549060ff82161560ff81159384845260081c16159160208315910152916131a4575b5061319e575f52600d60205260405f205490811561319e57600c60205260405f20905f5260205260405f2054670de0b6b3a764000090818102918183041490151715612f23570490565b50505f90565b90505f613154565b5f52600e6020526131c460405f205460035490612f7a565b4211156131cd57565b60046040517fb7e57455000000000000000000000000000000000000000000000000000000008152fd5b90613200612f50565b9062093a808201809211612f2357805f52602090600f8252604093845f20845f52835260ff855f205416613267575b505f52600f8152825f20915f52525f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008154169055565b815f52600b8352845f20845f52835282855f20918651809384918482549182815201915f52845f20905f5b8181106133d757505050906132a8910384612e88565b8251905f935f5b88878b8684106133155750955050505050505f52600b8352845f20845f528352845f2080545f8255806132fc575b5050835f52600d83526132f4855f2091825461304b565b90555f61322f565b61330e915f52845f209081019061308c565b5f806132dd565b5f6001959697989973ffffffffffffffffffffffffffffffffffffffff928361333e888b61301f565b511695858452600a9c8d81528285208286528152828520888652815261338a8a9485858820015490848852600c84528588208b8952845285882061338383825461304b565b9055612f7a565b8786529d8152828520918552528083208684528e528220828155015586167f8e9be633930d447edb768a7d9ee161eb92b33522fcca02370272cc378b720ddf5f80a40190869392916132af565b825473ffffffffffffffffffffffffffffffffffffffff16845289968895509093019260019283019201613292565b919290613411612f50565b915f9362093a8084018411612f2357855190604051927fe7e242d400000000000000000000000000000000000000000000000000000000845284600485015260208460248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb165afa9384156135fd575f946138c5575b50831561389b575f97889485949392915b8486106136325750505050505061271081116136085761352a575b60409362093a8083018452600d6020526134e7858520918254612f7a565b90558252600f60205262093a80838320910182526020522060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb163b15610b5457604051937ffd4a77f10000000000000000000000000000000000000000000000000000000085528160048601525f856024818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb165af19485156135fd576040956135ea575b5093506134c9565b6135f5919450612e74565b5f925f6135e2565b6040513d5f823e3d90fd5b60046040517fb658683c000000000000000000000000000000000000000000000000000000008152fd5b9091929394999573ffffffffffffffffffffffffffffffffffffffff6136588c8461301f565b51169a8b15613871578b5f52601060205260405f206040519061367a82612e0f565b549060ff6020818416159283158152019260081c161515825261384757511561381d576136d66136d0926127106136bb6136b4858a61301f565b5188612f10565b049384916136c9858a61301f565b5190612f7a565b99612f7a565b9b895f52600b60205260405f2062093a808c015f5260205260405f2080549368010000000000000000851015612e2b578c8c9261371b60019788928382018155612d7c565b81549060031b9073ffffffffffffffffffffffffffffffffffffffff88831b921b191617905562093a8082015f52600c60205260405f20855f5260205260405f20613767848254612f7a565b9055613773868b61301f565b51916040519261378284612e0f565b83526020830190848252855f52600a60205262093a8060405f2091015f5260205260405f20865f5260205260405f2092518355519101556137c3848961301f565b519060405191428352602083015260408201527f2e6c94de62679b33b86aa0615e0826812095a06925ae30e3fa2234858f0ca986606073ffffffffffffffffffffffffffffffffffffffff8b1692a40194939291906134ae565b60046040517f6cd7ad06000000000000000000000000000000000000000000000000000000008152fd5b60046040517f7c905419000000000000000000000000000000000000000000000000000000008152fd5b60046040517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b60046040517f7c176b74000000000000000000000000000000000000000000000000000000008152fd5b9093506020813d6020116138f1575b816138e160209383612e88565b81010312610b545751925f61349d565b3d91506138d456fea164736f6c6343000818000a

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000010b34b53b5989aa893c18e7fad4c460f98ce3847000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba

-----Decoded View---------------
Arg [0] : _owner (address): 0x10b34B53b5989Aa893c18e7fAD4c460f98cE3847
Arg [1] : _ve (address): 0xfAA874334d781cC69ea7F130613D2EdFE89b71fb
Arg [2] : _baseAsset (address): 0x4D85bA8c3918359c78Ed09581E5bc7578ba932ba

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 00000000000000000000000010b34b53b5989aa893c18e7fad4c460f98ce3847
Arg [1] : 000000000000000000000000faa874334d781cc69ea7f130613d2edfe89b71fb
Arg [2] : 0000000000000000000000004d85ba8c3918359c78ed09581e5bc7578ba932ba


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.