HYPE Price: $37.82 (-2.01%)

Contract

0xD1F03DdbcF8DDF201Dc49E834b33Ff842abd76E0

Overview

HYPE Balance

HyperEvm LogoHyperEvm LogoHyperEvm Logo0 HYPE

HYPE Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Withdraw78067792025-07-07 21:59:284 hrs ago1751925568IN
0xD1F03Ddb...42abd76E0
0 HYPE0.00002670.1
Withdraw78049772025-07-07 21:29:564 hrs ago1751923796IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000082780.31
Deposit77987012025-07-07 19:47:026 hrs ago1751917622IN
0xD1F03Ddb...42abd76E0
0 HYPE0.0038736213.87
Withdraw77936762025-07-07 18:24:408 hrs ago1751912680IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000162890.61
Withdraw77926442025-07-07 18:07:458 hrs ago1751911665IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000029370.11
Deposit77878412025-07-07 16:49:009 hrs ago1751906940IN
0xD1F03Ddb...42abd76E0
0 HYPE0.00003630.13
Deposit77874352025-07-07 16:42:219 hrs ago1751906541IN
0xD1F03Ddb...42abd76E0
0 HYPE0.00003630.13
Deposit77859582025-07-07 16:18:0810 hrs ago1751905088IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000036110.11
Deposit77787802025-07-07 14:20:2812 hrs ago1751898028IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000045960.14
Deposit77759322025-07-07 13:33:4712 hrs ago1751895227IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000033660.10254075
Withdraw77745552025-07-07 13:11:1213 hrs ago1751893872IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000032040.12
Deposit77706742025-07-07 12:07:3514 hrs ago1751890055IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000033510.12
Deposit77700762025-07-07 11:57:4714 hrs ago1751889467IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000042680.13
Deposit77673752025-07-07 11:13:3015 hrs ago1751886810IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000033510.12
Deposit77671332025-07-07 11:09:3215 hrs ago1751886572IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000033510.12
Deposit77664812025-07-07 10:58:5115 hrs ago1751885931IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000036110.11
Withdraw77556232025-07-07 8:00:5118 hrs ago1751875251IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000325811.22
Withdraw77319672025-07-07 1:33:0224 hrs ago1751851982IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000062230.21
Withdraw77311922025-07-07 1:20:2025 hrs ago1751851220IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000035560.12
Withdraw77301112025-07-07 1:02:3725 hrs ago1751850157IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000032040.12
Withdraw77215172025-07-06 22:41:4427 hrs ago1751841704IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000042720.16
Deposit77183102025-07-06 21:49:0928 hrs ago1751838549IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000101770.31
Deposit77170122025-07-06 21:27:5328 hrs ago1751837273IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000103330.37
Deposit77164972025-07-06 21:19:2629 hrs ago1751836766IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000050150.16
Withdraw77157342025-07-06 21:06:5629 hrs ago1751836016IN
0xD1F03Ddb...42abd76E0
0 HYPE0.000029370.11
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
37650992025-05-13 12:18:0055 days ago1747138680  Contract Creation0 HYPE
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Gauge

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 9 : Gauge.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SystemEpoch} from "./SystemEpoch.sol";
import {IBribe} from "../interfaces/IBribe.sol";
import {IGauge} from "../interfaces/IGauge.sol";
import {IVoter} from "../interfaces/IVoter.sol";
import {IVotingEscrow} from "../interfaces/IVotingEscrow.sol";
import {ICurveGauge} from "../interfaces/ICurveGauge.sol";

// Gauges are used to incentivize pools, they emit reward tokens over 7 days for staked LP tokens
contract Gauge is IGauge, SystemEpoch {
    address public immutable stake; // the LP token that needs to be staked for rewards
    address public immutable _ve; // the ve token used for gauges
    address public immutable internal_bribe;
    address public immutable external_bribe;
    address public immutable voter;

    uint256 public derivedSupply;
    mapping(address => uint256) public derivedBalances;

    bool public isForPair;

    uint256 internal constant PRECISION = 10 ** 18;
    uint256 internal constant MAX_REWARD_TOKENS = 16;

    // default snx staking contract implementation
    mapping(address => uint256) public rewardRate;
    mapping(address => uint256) public periodFinish;
    mapping(address => uint256) public lastUpdateTime;
    mapping(address => uint256) public rewardPerTokenStored;

    mapping(address => mapping(address => uint256)) public lastEarn;
    mapping(address => mapping(address => uint256)) public userRewardPerTokenStored;

    mapping(address => uint256) public tokenIds;

    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;

    address public staking;
    address[] public rewards;
    address[8] public stakingRewards;
    uint8 private _stakingRewardsCount;
    mapping(address => bool) public isReward;
    mapping(address => bool) public isStakingReward;

    /// @notice A checkpoint for marking balance
    struct Checkpoint {
        uint256 timestamp;
        uint256 balanceOf;
    }

    /// @notice A checkpoint for marking reward rate
    struct RewardPerTokenCheckpoint {
        uint256 timestamp;
        uint256 rewardPerToken;
    }

    /// @notice A checkpoint for marking supply
    struct SupplyCheckpoint {
        uint256 timestamp;
        uint256 supply;
    }

    /// @notice A record of balance checkpoints for each account, by index
    mapping(address => mapping(uint256 => Checkpoint)) public checkpoints;
    /// @notice The number of checkpoints for each account
    mapping(address => uint256) public numCheckpoints;
    /// @notice A record of balance checkpoints for each token, by index
    mapping(uint256 => SupplyCheckpoint) public supplyCheckpoints;
    /// @notice The number of checkpoints
    uint256 public supplyNumCheckpoints;
    /// @notice A record of balance checkpoints for each token, by index
    mapping(address => mapping(uint256 => RewardPerTokenCheckpoint)) public rewardPerTokenCheckpoints;
    /// @notice The number of checkpoints for each token
    mapping(address => uint256) public rewardPerTokenNumCheckpoints;

    event Deposit(address indexed from, uint256 tokenId, uint256 amount);
    event Withdraw(address indexed from, uint256 tokenId, uint256 amount);
    event NotifyReward(address indexed from, address indexed reward, uint256 amount);
    event ClaimFees(address indexed from, uint256 claimed0, uint256 claimed1);
    event ClaimRewards(address indexed from, address indexed reward, uint256 amount);

    constructor(
        address _stake,
        address _staking,
        address _internal_bribe,
        address _external_bribe,
        address __ve,
        address _voter,
        bool _forPair,
        address[] memory _allowedRewardTokens
    ) {
        stake = _stake;
        staking = _staking;
        internal_bribe = _internal_bribe;
        external_bribe = _external_bribe;
        _ve = __ve;
        voter = _voter;
        isForPair = _forPair;

        for (uint256 i; i < _allowedRewardTokens.length; i++) {
            if (_allowedRewardTokens[i] != address(0)) {
                isReward[_allowedRewardTokens[i]] = true;
                rewards.push(_allowedRewardTokens[i]);
            }
        }

        if (_staking != address(0)) {
            ICurveGauge(_staking).set_rewards_receiver(IVotingEscrow(__ve).team());
        }
    }

    // simple re-entrancy check
    uint256 internal _unlocked = 1;

    modifier lock() {
        require(_unlocked == 1);
        _unlocked = 2;
        _;
        _unlocked = 1;
    }

    function claimFees() external lock {
        require(msg.sender == IVotingEscrow(_ve).team(), "only team");

        if (staking == address(0)) {
            return;
        }

        ICurveGauge(staking).claim_rewards(address(this), address(0));
    }

    /**
     * @notice Determine the prior balance for an account as of a block number
     * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
     * @param account The address of the account to check
     * @param timestamp The timestamp to get the balance at
     * @return The balance the account had as of the given block
     */
    function getPriorBalanceIndex(address account, uint256 timestamp) public view returns (uint256) {
        uint256 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (checkpoints[account][0].timestamp > timestamp) {
            return 0;
        }

        uint256 lower = 0;
        uint256 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPriorSupplyIndex(uint256 timestamp) public view returns (uint256) {
        uint256 nCheckpoints = supplyNumCheckpoints;
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (supplyCheckpoints[nCheckpoints - 1].timestamp <= timestamp) {
            return (nCheckpoints - 1);
        }

        // Next check implicit zero balance
        if (supplyCheckpoints[0].timestamp > timestamp) {
            return 0;
        }

        uint256 lower = 0;
        uint256 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            SupplyCheckpoint memory cp = supplyCheckpoints[center];
            if (cp.timestamp == timestamp) {
                return center;
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return lower;
    }

    function getPriorRewardPerToken(address token, uint256 timestamp) public view returns (uint256, uint256) {
        uint256 nCheckpoints = rewardPerTokenNumCheckpoints[token];
        if (nCheckpoints == 0) {
            return (0, 0);
        }

        // First check most recent balance
        if (rewardPerTokenCheckpoints[token][nCheckpoints - 1].timestamp <= timestamp) {
            return (
                rewardPerTokenCheckpoints[token][nCheckpoints - 1].rewardPerToken,
                rewardPerTokenCheckpoints[token][nCheckpoints - 1].timestamp
            );
        }

        // Next check implicit zero balance
        if (rewardPerTokenCheckpoints[token][0].timestamp > timestamp) {
            return (0, 0);
        }

        uint256 lower = 0;
        uint256 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            RewardPerTokenCheckpoint memory cp = rewardPerTokenCheckpoints[token][center];
            if (cp.timestamp == timestamp) {
                return (cp.rewardPerToken, cp.timestamp);
            } else if (cp.timestamp < timestamp) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return
            (rewardPerTokenCheckpoints[token][lower].rewardPerToken, rewardPerTokenCheckpoints[token][lower].timestamp);
    }

    function _writeCheckpoint(address account, uint256 balance) internal {
        uint256 _timestamp = block.timestamp;
        uint256 _nCheckPoints = numCheckpoints[account];

        if (_nCheckPoints > 0 && checkpoints[account][_nCheckPoints - 1].timestamp == _timestamp) {
            checkpoints[account][_nCheckPoints - 1].balanceOf = balance;
        } else {
            checkpoints[account][_nCheckPoints] = Checkpoint(_timestamp, balance);
            numCheckpoints[account] = _nCheckPoints + 1;
        }
    }

    function _writeRewardPerTokenCheckpoint(address token, uint256 reward, uint256 timestamp) internal {
        uint256 _nCheckPoints = rewardPerTokenNumCheckpoints[token];

        if (_nCheckPoints > 0 && rewardPerTokenCheckpoints[token][_nCheckPoints - 1].timestamp == timestamp) {
            rewardPerTokenCheckpoints[token][_nCheckPoints - 1].rewardPerToken = reward;
        } else {
            rewardPerTokenCheckpoints[token][_nCheckPoints] = RewardPerTokenCheckpoint(timestamp, reward);
            rewardPerTokenNumCheckpoints[token] = _nCheckPoints + 1;
        }
    }

    function _writeSupplyCheckpoint() internal {
        uint256 _nCheckPoints = supplyNumCheckpoints;
        uint256 _timestamp = block.timestamp;

        if (_nCheckPoints > 0 && supplyCheckpoints[_nCheckPoints - 1].timestamp == _timestamp) {
            supplyCheckpoints[_nCheckPoints - 1].supply = derivedSupply;
        } else {
            supplyCheckpoints[_nCheckPoints] = SupplyCheckpoint(_timestamp, derivedSupply);
            supplyNumCheckpoints = _nCheckPoints + 1;
        }
    }

    function rewardsListLength() external view returns (uint256) {
        return rewards.length;
    }

    // returns the last time the reward was modified or periodFinish if the reward has ended
    function lastTimeRewardApplicable(address token) public view returns (uint256) {
        return FixedPointMathLib.min(block.timestamp, periodFinish[token]);
    }

    function getReward(address account, address[] memory tokens) external lock {
        require(msg.sender == account || msg.sender == voter);
        _unlocked = 1;
        IVoter(voter).distribute(address(this));
        _unlocked = 2;

        for (uint256 i = 0; i < tokens.length; i++) {
            (rewardPerTokenStored[tokens[i]], lastUpdateTime[tokens[i]]) =
                _updateRewardPerToken(tokens[i], type(uint256).max, true);

            uint256 _reward = earned(tokens[i], account);
            lastEarn[tokens[i]][account] = block.timestamp;
            userRewardPerTokenStored[tokens[i]][account] = rewardPerTokenStored[tokens[i]];
            if (_reward > 0) _safeTransfer(tokens[i], account, _reward);

            emit ClaimRewards(msg.sender, tokens[i], _reward);
        }

        uint256 _derivedBalance = derivedBalances[account];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(account);
        derivedBalances[account] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(account, derivedBalances[account]);
        _writeSupplyCheckpoint();
    }

    function rewardPerToken(address token) public view returns (uint256) {
        if (derivedSupply == 0) {
            return rewardPerTokenStored[token];
        }
        return rewardPerTokenStored[token]
            + (
                (lastTimeRewardApplicable(token) - FixedPointMathLib.min(lastUpdateTime[token], periodFinish[token]))
                    * rewardRate[token] * PRECISION / derivedSupply
            );
    }

    function derivedBalance(address account) public view returns (uint256) {
        return balanceOf[account];
    }

    function batchRewardPerToken(address token, uint256 maxRuns) external {
        (rewardPerTokenStored[token], lastUpdateTime[token]) = _batchRewardPerToken(token, maxRuns);
    }

    function _batchRewardPerToken(address token, uint256 maxRuns) internal returns (uint256, uint256) {
        uint256 _startTimestamp = lastUpdateTime[token];
        uint256 reward = rewardPerTokenStored[token];

        if (supplyNumCheckpoints == 0) {
            return (reward, _startTimestamp);
        }

        if (rewardRate[token] == 0) {
            return (reward, block.timestamp);
        }

        uint256 _startIndex = getPriorSupplyIndex(_startTimestamp);
        uint256 _endIndex = FixedPointMathLib.min(supplyNumCheckpoints - 1, maxRuns);

        for (uint256 i = _startIndex; i < _endIndex; i++) {
            SupplyCheckpoint memory sp0 = supplyCheckpoints[i];
            if (sp0.supply > 0) {
                SupplyCheckpoint memory sp1 = supplyCheckpoints[i + 1];
                (uint256 _reward, uint256 _endTime) =
                    _calcRewardPerToken(token, sp1.timestamp, sp0.timestamp, sp0.supply, _startTimestamp);
                reward += _reward;
                _writeRewardPerTokenCheckpoint(token, reward, _endTime);
                _startTimestamp = _endTime;
            }
        }

        return (reward, _startTimestamp);
    }

    function _calcRewardPerToken(
        address token,
        uint256 timestamp1,
        uint256 timestamp0,
        uint256 supply,
        uint256 startTimestamp
    ) internal view returns (uint256, uint256) {
        uint256 endTime = FixedPointMathLib.max(timestamp1, startTimestamp);
        return (
            (
                (
                    FixedPointMathLib.min(endTime, periodFinish[token])
                        - FixedPointMathLib.min(FixedPointMathLib.max(timestamp0, startTimestamp), periodFinish[token])
                ) * rewardRate[token] * PRECISION / supply
            ),
            endTime
        );
    }

    /// @dev Update stored rewardPerToken values without the last one snapshot
    ///      If the contract will get "out of gas" error on users actions this will be helpful
    function batchUpdateRewardPerToken(address token, uint256 maxRuns) external {
        (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, maxRuns, false);
    }

    function _updateRewardForAllTokens() internal {
        uint256 length = rewards.length;
        for (uint256 i; i < length; i++) {
            address token = rewards[i];
            (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint256).max, true);
        }
    }

    function _updateRewardPerToken(address token, uint256 maxRuns, bool actualLast)
        internal
        returns (uint256, uint256)
    {
        uint256 _startTimestamp = lastUpdateTime[token];
        uint256 reward = rewardPerTokenStored[token];

        if (supplyNumCheckpoints == 0) {
            return (reward, _startTimestamp);
        }

        if (rewardRate[token] == 0) {
            return (reward, block.timestamp);
        }

        uint256 _startIndex = getPriorSupplyIndex(_startTimestamp);
        uint256 _endIndex = FixedPointMathLib.min(supplyNumCheckpoints - 1, maxRuns);

        if (_endIndex > 0) {
            for (uint256 i = _startIndex; i <= _endIndex - 1; i++) {
                SupplyCheckpoint memory sp0 = supplyCheckpoints[i];
                if (sp0.supply > 0) {
                    SupplyCheckpoint memory sp1 = supplyCheckpoints[i + 1];
                    (uint256 _reward, uint256 _endTime) =
                        _calcRewardPerToken(token, sp1.timestamp, sp0.timestamp, sp0.supply, _startTimestamp);
                    reward += _reward;
                    _writeRewardPerTokenCheckpoint(token, reward, _endTime);
                    _startTimestamp = _endTime;
                }
            }
        }

        // need to override the last value with actual numbers only on deposit/withdraw/claim/notify actions
        if (actualLast) {
            SupplyCheckpoint memory sp = supplyCheckpoints[_endIndex];
            if (sp.supply > 0) {
                (uint256 _reward,) = _calcRewardPerToken(
                    token,
                    lastTimeRewardApplicable(token),
                    FixedPointMathLib.max(sp.timestamp, _startTimestamp),
                    sp.supply,
                    _startTimestamp
                );
                reward += _reward;
                _writeRewardPerTokenCheckpoint(token, reward, block.timestamp);
                _startTimestamp = block.timestamp;
            }
        }

        return (reward, _startTimestamp);
    }

    // earned is an estimation, it won't be exact till the supply > rewardPerToken calculations have run
    function earned(address token, address account) public view returns (uint256) {
        uint256 _startTimestamp =
            FixedPointMathLib.max(lastEarn[token][account], rewardPerTokenCheckpoints[token][0].timestamp);
        if (numCheckpoints[account] == 0) {
            return 0;
        }

        uint256 _startIndex = getPriorBalanceIndex(account, _startTimestamp);
        uint256 _endIndex = numCheckpoints[account] - 1;

        uint256 reward = 0;

        if (_endIndex > 0) {
            for (uint256 i = _startIndex; i <= _endIndex - 1; i++) {
                Checkpoint memory cp0 = checkpoints[account][i];
                Checkpoint memory cp1 = checkpoints[account][i + 1];
                (uint256 _rewardPerTokenStored0,) = getPriorRewardPerToken(token, cp0.timestamp);
                (uint256 _rewardPerTokenStored1,) = getPriorRewardPerToken(token, cp1.timestamp);
                reward += cp0.balanceOf * (_rewardPerTokenStored1 - _rewardPerTokenStored0) / PRECISION;
            }
        }

        Checkpoint memory cp = checkpoints[account][_endIndex];
        (uint256 _rewardPerTokenStored,) = getPriorRewardPerToken(token, cp.timestamp);
        reward += cp.balanceOf
            * (
                rewardPerToken(token)
                    - FixedPointMathLib.max(_rewardPerTokenStored, userRewardPerTokenStored[token][account])
            ) / PRECISION;

        return reward;
    }

    function depositAll(uint256 tokenId) external {
        deposit(IERC20(stake).balanceOf(msg.sender), tokenId);
    }

    function deposit(uint256 amount, uint256 tokenId) public lock {
        require(amount > 0);
        _updateRewardForAllTokens();

        _safeTransferFrom(stake, msg.sender, address(this), amount);

        address stakingAddress = staking;
        if (stakingAddress != address(0)) {
            IERC20(stake).approve(stakingAddress, amount);
            ICurveGauge(stakingAddress).deposit(amount);
        }

        totalSupply += amount;
        balanceOf[msg.sender] += amount;

        if (tokenId > 0) {
            require(IVotingEscrow(_ve).ownerOf(tokenId) == msg.sender);
            if (tokenIds[msg.sender] == 0) {
                tokenIds[msg.sender] = tokenId;
                IVoter(voter).attachTokenToGauge(tokenId, msg.sender);
            }
            require(tokenIds[msg.sender] == tokenId);
        } else {
            tokenId = tokenIds[msg.sender];
        }

        uint256 _derivedBalance = derivedBalances[msg.sender];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(msg.sender);
        derivedBalances[msg.sender] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(msg.sender, _derivedBalance);
        _writeSupplyCheckpoint();

        IVoter(voter).emitDeposit(tokenId, msg.sender, amount);
        emit Deposit(msg.sender, tokenId, amount);
    }

    function withdrawAll() external {
        withdraw(balanceOf[msg.sender]);
    }

    function withdraw(uint256 amount) public {
        uint256 tokenId = 0;
        if (amount == balanceOf[msg.sender]) {
            tokenId = tokenIds[msg.sender];
        }
        withdrawToken(amount, tokenId);
    }

    function withdrawToken(uint256 amount, uint256 tokenId) public lock {
        _updateRewardForAllTokens();

        totalSupply -= amount;
        balanceOf[msg.sender] -= amount;
        if (staking != address(0)) {
            ICurveGauge(staking).withdraw(amount);
        }
        _safeTransfer(stake, msg.sender, amount);

        if (tokenId > 0) {
            require(tokenId == tokenIds[msg.sender]);
            tokenIds[msg.sender] = 0;
            IVoter(voter).detachTokenFromGauge(tokenId, msg.sender);
        } else {
            tokenId = tokenIds[msg.sender];
        }

        uint256 _derivedBalance = derivedBalances[msg.sender];
        derivedSupply -= _derivedBalance;
        _derivedBalance = derivedBalance(msg.sender);
        derivedBalances[msg.sender] = _derivedBalance;
        derivedSupply += _derivedBalance;

        _writeCheckpoint(msg.sender, derivedBalances[msg.sender]);
        _writeSupplyCheckpoint();

        IVoter(voter).emitWithdraw(tokenId, msg.sender, amount);
        emit Withdraw(msg.sender, tokenId, amount);
    }

    function left(address token) external view returns (uint256) {
        if (block.timestamp >= periodFinish[token]) return 0;
        uint256 _remaining = periodFinish[token] - block.timestamp;
        return _remaining * rewardRate[token];
    }

    function notifyRewardAmount(address token, uint256 amount) external lock {
        require(token != stake);
        require(amount > 0);
        if (!isReward[token]) {
            require(IVoter(voter).isWhitelisted(token), "rewards tokens must be whitelisted");
            require(rewards.length < MAX_REWARD_TOKENS, "too many rewards tokens");
        }
        if (rewardRate[token] == 0) _writeRewardPerTokenCheckpoint(token, 0, block.timestamp);
        (rewardPerTokenStored[token], lastUpdateTime[token]) = _updateRewardPerToken(token, type(uint256).max, true);

        if (block.timestamp >= periodFinish[token]) {
            _safeTransferFrom(token, msg.sender, address(this), amount);
            rewardRate[token] = amount / EPOCH;
        } else {
            uint256 _remaining = periodFinish[token] - block.timestamp;
            uint256 _left = _remaining * rewardRate[token];
            require(amount > _left);
            _safeTransferFrom(token, msg.sender, address(this), amount);
            rewardRate[token] = (amount + _left) / EPOCH;
        }
        require(rewardRate[token] > 0);
        uint256 balance = IERC20(token).balanceOf(address(this));
        require(rewardRate[token] <= balance / EPOCH, "Provided reward too high");
        periodFinish[token] = block.timestamp + EPOCH;
        if (!isReward[token]) {
            isReward[token] = true;
            rewards.push(token);
        }

        emit NotifyReward(msg.sender, token, amount);
    }

    function swapOutRewardToken(uint256 i, address oldToken, address newToken) external {
        require(msg.sender == IVotingEscrow(_ve).team(), "only team");
        require(rewards[i] == oldToken);
        isReward[oldToken] = false;
        isReward[newToken] = true;
        rewards[i] = newToken;
    }

    function enableStaking(address _newStaking, address _rewardsReceiver) external {
        require(msg.sender == IVotingEscrow(_ve).team(), "only team");
        require(staking == address(0), "staking already exists");

        staking = _newStaking;
        ICurveGauge(_newStaking).set_rewards_receiver(_rewardsReceiver);

        IERC20 stakeToken = IERC20(stake);
        uint256 balance = stakeToken.balanceOf(address(this));

        if (balance > 0) {
            stakeToken.approve(address(_newStaking), balance);
            ICurveGauge(_newStaking).deposit(balance);
        }
    }

    function setRewardsReceiver(address _rewardsReceiver) external {
        require(msg.sender == IVotingEscrow(_ve).team(), "only team");
        require(staking != address(0), "staking already exists");

        ICurveGauge(staking).set_rewards_receiver(_rewardsReceiver);
    }

    function _safeTransfer(address token, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeTransferFrom(address token, address from, address to, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }

    function _safeApprove(address token, address spender, uint256 value) internal {
        require(token.code.length > 0);
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

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

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

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

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    /// Note: This function is an approximation.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use Newton-Raphson iteration to improve the precision.
                    // Thanks to Hensel's lifting lemma, this also works in modular
                    // arithmetic, doubling the correct bits in each step.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x + y)`.
    function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(0, lt(add(x, y), x)), add(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x * y)`.
    function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `x != 0 ? x : y`, without branching.
    function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != bytes32(0) ? x : y`, without branching.
    function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != address(0) ? x : y`, without branching.
    function coalesce(address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(shl(96, x))))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`, rounded down.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

File 4 of 9 : SystemEpoch.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

abstract contract SystemEpoch {
    uint256 public constant EPOCH = 7 days;
}

//// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IBribe {
    function _deposit(uint256 amount, uint256 tokenId) external;
    function _withdraw(uint256 amount, uint256 tokenId) external;
    function getRewardForOwner(uint256 tokenId, address[] memory tokens) external;
    function notifyRewardAmount(address token, uint256 amount) external;
    function left(address token) external view returns (uint256);
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface ICurveGauge {
    function deposit(uint256 _amount) external;

    function withdraw(uint256 _amount) external;

    function claim_rewards(address account, address receiver) external;

    function set_rewards_receiver(address receiver) external;
}

//// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IGauge {
    function notifyRewardAmount(address token, uint256 amount) external;
    function getReward(address account, address[] memory tokens) external;
    function left(address token) external view returns (uint256);
    function isForPair() external view returns (bool);
}

//// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IVoter {
    function _ve() external view returns (address);
    function governor() external view returns (address);
    function emergencyCouncil() external view returns (address);
    function attachTokenToGauge(uint256 _tokenId, address account) external;
    function detachTokenFromGauge(uint256 _tokenId, address account) external;
    function emitDeposit(uint256 _tokenId, address account, uint256 amount) external;
    function emitWithdraw(uint256 _tokenId, address account, uint256 amount) external;
    function isWhitelisted(address token) external view returns (bool);
    function notifyRewardAmount(uint256 amount) external;
    function distribute(address _gauge) external;
}

//// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

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

    struct LockedBalance {
        int128 amount;
        uint256 end;
        bool perpetuallyLocked;
    }

    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 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 create_lock_for(uint256, uint256, address) external returns (uint256);

    function balanceOfNFT(uint256) external view returns (uint256);
    function totalSupply() external view returns (uint256);

    function locked(uint256 id) external view returns (LockedBalance memory);
}

Settings
{
  "evmVersion": "cancun",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "ipfs",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
    "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
    "solady/=node_modules/solady/src/",
    "forge-std/=node_modules/forge-std/src/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_stake","type":"address"},{"internalType":"address","name":"_staking","type":"address"},{"internalType":"address","name":"_internal_bribe","type":"address"},{"internalType":"address","name":"_external_bribe","type":"address"},{"internalType":"address","name":"__ve","type":"address"},{"internalType":"address","name":"_voter","type":"address"},{"internalType":"bool","name":"_forPair","type":"bool"},{"internalType":"address[]","name":"_allowedRewardTokens","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"claimed0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimed1","type":"uint256"}],"name":"ClaimFees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"reward","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NotifyReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"EPOCH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_ve","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxRuns","type":"uint256"}],"name":"batchRewardPerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxRuns","type":"uint256"}],"name":"batchUpdateRewardPerToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"checkpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"balanceOf","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"depositAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"derivedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"derivedBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"derivedSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newStaking","type":"address"},{"internalType":"address","name":"_rewardsReceiver","type":"address"}],"name":"enableStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"external_bribe","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorBalanceIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorRewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getPriorSupplyIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"getReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"internal_bribe","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isForPair","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isStakingReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"lastEarn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"left","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardPerTokenCheckpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"rewardPerToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardPerTokenNumCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsListLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsReceiver","type":"address"}],"name":"setRewardsReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stake","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"staking","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakingRewards","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"supplyCheckpoints","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyNumCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"address","name":"oldToken","type":"address"},{"internalType":"address","name":"newToken","type":"address"}],"name":"swapOutRewardToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawToken","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101208060405234610387576137f3803803809161001d82856103dd565b83398101610100828203126103875761003582610400565b61004160208401610400565b9261004e60408201610400565b9161005b60608301610400565b61006760808401610400565b9561007460a08501610400565b9260c0850151948515158096036103875760e0810151906001600160401b03821161038757019680601f89011215610387578751976001600160401b038911610210578860051b90604051996100cd602084018c6103dd565b8a526020808b019282010192831161038757602001905b8282106103c5575050506001601f5560805260018060a01b0316938460018060a01b0319600c541617600c5560c05260e0528460a0526101005260ff8019600254169116176002555f5b8251811015610224576001600160a01b036101498285610414565b5116610158575b60010161012e565b6001600160a01b0361016a8285610414565b51165f908152601760205260409020805460ff191660011790556001600160a01b036101968285610414565b511690600d549168010000000000000000831015610210576001830180600d558310156101fc57600d5f527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb590920180546001600160a01b031916909217909155610150565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b5082816102f5575b6040516133ca908161042982396080518181816102bb015281816106af01528181610b4401528181610cbf0152818161171401528181611d57015261254f015260a051818181610739015281816112fd015281816113e801528181611b5a01528181611c860152612905015260c05181611453015260e05181611f73015261010051818181610309015281816103cf015281816107ff015281816108ca015281816110af0152818161167c015281816117f401528181611a2a0152818161259c01526126640152f35b6040516342f9577960e11b815290602090829060049082905f906001600160a01b03165af190811561037c575f9161038b575b50813b1561038757604051635efcc08b60e11b81526001600160a01b039091166004820152905f908290602490829084905af1801561037c5761036c575b8061022c565b5f610376916103dd565b5f610366565b6040513d5f823e3d90fd5b5f80fd5b90506020813d6020116103bd575b816103a6602093836103dd565b81010312610387576103b790610400565b82610328565b3d9150610399565b602080916103d284610400565b8152019101906100e4565b601f909101601f19168101906001600160401b0382119082101761021057604052565b51906001600160a01b038216820361038757565b80518210156101fc5760209160051b01019056fe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301316ddf14611fa25750806303fbf83a14611f5e5780630cdfebfa14611f11578063115c6f3914611eea57806318160ddd14611ecd578063211dc32d14611ea157806321cc096814611c4a578063221ca18c14611c115780632619582614611b295780632ce9aead14611af05780632e1a7d4d14611ad35780632f85074a14611a9457806331279d3d146117435780633a4b66f1146116fe5780633ca068b6146116ab57806346c96aac146116665780634cf088d91461163d5780634d5ce038146115fe578063597a8d6f146115c65780635a45d052146115a0578063638634ee1461156157806363fb415b1461152857806368fcee1a146114da5780636fcfff45146114a157806370a0823114610ab057806376f4be3614611482578063770f85711461143d578063853828b6146114175780638dd598fb146113d25780639418f939146112b257806399bcc0521461128e5780639ce43f9014611255578063a0dc275814611237578063a495e5b5146111e4578063aa479652146111ab578063b66503cf14610c84578063c6f678bd14610b19578063d294f09314610ae9578063d35e254414610ab0578063d7da4bb014610a93578063da09d19d14610a5a578063e2bbb1581461067e578063e57482131461065b578063e68863961461063d578063e8111a121461061f578063f1229777146105f3578063f301af42146105af578063f7412baf1461057b578063fc97a30314610542578063fd3140981461050e5763fdb483c71461024f575f80fd5b3461050b5761025d3661206b565b906001601f5403610488576002601f55816102766132be565b61028282600a546120ad565b600a55338452600b6020526040842061029c8382546120ad565b9055600c5484906001600160a01b0316806104c4575b50506102df82337f0000000000000000000000000000000000000000000000000000000000000000612eb8565b156104b05733835260096020526040832054820361048857338352600960205260408320839055827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104975760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561048c5761049b575b50505b3383526001602052610385604084205484546120ad565b8355338352600b6020526103ac604084205433855260016020528060408620558454612229565b835533835260016020526103c4604084205433612f4b565b6103cc61301b565b827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b1561049757604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c57610473575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601f5580f35b8161047d91612049565b61048857825f610438565b8280fd5b6040513d84823e3d90fd5b5080fd5b816104a591612049565b61048857825f61036b565b33835260096020526040832054915061036e565b803b1561049757818091602460405180948193632e1a7d4d60e01b83528960048401525af1801561048c57156102b257816104fe91612049565b61050957835f6102b2565b835b80fd5b503461050b57604036600319011261050b57604061053661052d611fed565b60243590612a58565b82519182526020820152f35b503461050b57602036600319011261050b576020906040906001600160a01b0361056a611fed565b168152600983522054604051908152f35b503461050b57602036600319011261050b57604080916004358152601b602052206001815491015482519182526020820152f35b503461050b57602036600319011261050b5760043590600d5482101561050b5760206105da83612081565b905460405160039290921b1c6001600160a01b03168152f35b503461050b57602036600319011261050b576020610617610612611fed565b6129a9565b604051908152f35b503461050b578060031936011261050b576020601c54604051908152f35b503461050b578060031936011261050b576020600d54604051908152f35b503461050b578060031936011261050b57602060ff600254166040519015158152f35b503461050b5761068d3661206b565b906001601f5403610488576002601f55818115610509576106ac6132be565b837f00000000000000000000000000000000000000000000000000000000000000006106da8430338461326e565b600c546001600160a01b03169081610996575b5050506106fc82600a54612229565b600a55338452600b60205260408420610716838254612229565b905515610982576040516331a9108f60e11b8152600481018390526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610977578491610948575b50336001600160a01b03909116036104885733835260096020526040832054156108b8575b338352600960205281604084205403610488575b33835260016020526107c1604084205484546120ad565b8355338352600b6020526107f4604084205433855260016020528060408620556107ec818654612229565b855533612f4b565b6107fc61301b565b827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104975760405163530e389d60e11b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c576108a3575b505060405191825260208201527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1560403392a26001601f5580f35b816108ad91612049565b61048857825f610868565b338352600960205260408320829055827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104975760405163698473e360e01b8152600481018590523360248201529082908290604490829084905af1801561048c57610933575b5050610796565b8161093d91612049565b61048857825f61092c565b61096a915060203d602011610970575b6109628183612049565b810190612429565b5f610771565b503d610958565b6040513d86823e3d90fd5b3383526009602052604083205491506107aa565b60405163095ea7b360e01b81526001600160a01b038381166004830152602482018790529091602091839160449183918891165af18015610a4f57610a22575b50803b156104975781809160246040518094819363b6b55f2560e01b83528960048401525af1801561048c57610a0d575b806106ed565b81610a1791612049565b61050957835f610a07565b610a439060203d602011610a48575b610a3b8183612049565b8101906124c5565b6109d6565b503d610a31565b6040513d85823e3d90fd5b503461050b57602036600319011261050b576020906040906001600160a01b03610a82611fed565b168152600483522054604051908152f35b503461050b578060031936011261050b5760209054604051908152f35b503461050b57602036600319011261050b576020906040906001600160a01b03610ad8611fed565b168152600b83522054604051908152f35b503461050b578060031936011261050b576001601f540361050b576002601f55610b116128ef565b6001601f5580f35b503461050b57602036600319011261050b576040516370a0823160e01b8152336004828101919091527f0000000000000000000000000000000000000000000000000000000000000000919035836001600160a01b038416602084602481845afa93841561048c578294610c49575b506001601f5403610497576002601f55829484156104885784610bb691610bad6132be565b3090339061326e565b600c546001600160a01b03169081610bd7575050506106fc82600a54612229565b60405163095ea7b360e01b81526001600160a01b038316600482015260248101869052906020908290604490829087905af18015610a4f57610a225750803b156104975781809160246040518094819363b6b55f2560e01b83528960048401525af1801561048c57610a0d57806106ed565b915092506020813d602011610c7c575b81610c6660209383612049565b81010312610c7857849051925f610b88565b5f80fd5b3d9150610c59565b503461050b57604036600319011261050b57610c9e611fed565b6024356001601f5403610488576002601f556001600160a01b0382811692907f000000000000000000000000000000000000000000000000000000000000000016831461050957811561050957828452601760205260ff60408520541615611093575b8284526003602052604084205415610fa0575b610d1d81612bb8565b848652600660205260408620908587526005602052604087205555828452600460205260408420544210155f14610f3b57610d5a8230338461326e565b828452600360205262093a80820460408520555b8284526003602052604084205415610509576040516370a0823160e01b8152306004820152602081602481875afa908115610f30578591610efe575b50838552600360205262093a806040862054910410610eb95762093a804201804211610ea55783855260046020526040852055828452601760205260ff60408520541615610e28575b506040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a36001601f5580f35b828452601760205260408420805460ff19166001179055600d5468010000000000000000811015610e915790610e67826001610e8b9401600d55612081565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f610df3565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606490fd5b90506020813d602011610f28575b81610f1960209383612049565b81010312610c7857515f610daa565b3d9150610f0c565b6040513d87823e3d90fd5b8284526004602052610f67610f544260408720546120ad565b8486526003602052604086205490612236565b80831115610f9c57610f8962093a8091610f838530338761326e565b84612229565b0483855260036020526040852055610d6e565b8480fd5b828452601e602052604084205480151580611056575b15610ffa57838552601d60205260408520905f198101908111610fe6578552602052836001604082200155610d14565b634e487b7160e01b86526011600452602486fd5b61103960405161100981612019565b428152866020820152858752601d6020526040872083885260205260408720906020600191805184550151910155565b60018101809111610ea557838552601e6020526040852055610d14565b50838552601d602052604085205f19820182811161107f57865260205242604086205414610fb6565b634e487b7160e01b87526011600452602487fd5b604051633af32abf60e01b8152600481018490526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610f3057859161118c575b501561113c576010600d5410610d015760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608490fd5b6111a5915060203d602011610a4857610a3b8183612049565b5f6110e7565b503461050b57602036600319011261050b576020906040906001600160a01b036111d3611fed565b168152601e83522054604051908152f35b503461050b57604036600319011261050b576040611200611fed565b91611209612003565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461050b578060031936011261050b57602060405162093a808152f35b503461050b57602036600319011261050b576020906040906001600160a01b0361127d611fed565b168152600683522054604051908152f35b503461050b57602036600319011261050b5760206106176112ad611fed565b61289f565b503461050b57606036600319011261050b576004356112cf612003565b6044356001600160a01b03811692909190838303610f9c576040516342f9577960e11b8152602081600481897f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156113c7576113499187916113a8575b506001600160a01b03163314612448565b61135281612081565b90546001600160a01b039384169360039290921b1c16829003610f9c576113a593610e6792865260176020526040862060ff1981541690558552601760205260408520600160ff19825416179055612081565b80f35b6113c1915060203d602011610970576109628183612049565b5f611338565b6040513d88823e3d90fd5b503461050b578060031936011261050b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461050b578060031936011261050b57338152600b6020526113a560408220546124dd565b503461050b578060031936011261050b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461050b57602036600319011261050b5760206106176004356127b3565b503461050b57602036600319011261050b576020906040906001600160a01b036114c9611fed565b168152601a83522054604051908152f35b503461050b57604036600319011261050b576114f4611fed565b61150060243582612d4f565b909160018060a01b031690818452600660205260408420918452600560205260408420555580f35b503461050b57602036600319011261050b576020906040906001600160a01b03611550611fed565b168152600183522054604051908152f35b503461050b57602036600319011261050b576020610617611580611fed565b60018060a01b03165f52600460205260405f205442811090421802421890565b503461050b57604036600319011261050b576115ba611fed565b611500602435826130b1565b503461050b57602036600319011261050b57600435600881101561049757600e01546040516001600160a01b03909116815260209150f35b503461050b57602036600319011261050b5760209060ff906040906001600160a01b03611629611fed565b168152601784522054166040519015158152f35b503461050b578060031936011261050b57600c546040516001600160a01b039091168152602090f35b503461050b578060031936011261050b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461050b57604036600319011261050b5760406116c7611fed565b916116d0612003565b9260018060a01b031681526008602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461050b578060031936011261050b576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461050b57604036600319011261050b5761175d611fed565b60243567ffffffffffffffff811161048857366023820112156104885780600401359067ffffffffffffffff8211611a80578160051b90604051926117a56020840185612049565b83526024602084019282010190368211611a7c57602401915b818310611a58575050506001601f5403610488576001600160a01b038216903382148015611a26575b15610509576001601f55837f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610497578180916024604051809481936363453ae160e01b83523060048401525af1801561048c57611a11575b50506002601f55835b81518110156119b15760019061187d6001600160a01b03611876838661279f565b5116612bb8565b838060a01b0361188d848761279f565b5116885260066020526040882090848060a01b036118ab858861279f565b5116895260056020526040892055556118d585838060a01b036118ce848761279f565b5116612249565b828060a01b036118e5838661279f565b51168752600760205260408720855f5260205260405f20429055828060a01b0361190f838661279f565b5116875260066020526040872054838060a01b0361192d848761279f565b51168852600860205260408820865f5260205260405f205580611990575b828060a01b0361195b838661279f565b5116906040519081527f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc960203392a301611855565b6119ac8187858060a01b036119a5868961279f565b5116612eb8565b61194b565b84611a09858580845260016020526119ce604085205485546120ad565b8455808452600b6020526119f5604085205482865260016020528060408720558554612229565b845583526001602052604083205490612f4b565b610b1161301b565b81611a1b91612049565b61050957835f61184c565b50337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146117e7565b82356001600160a01b0381168103611a78578152602092830192016117be565b8680fd5b8580fd5b634e487b7160e01b84526041600452602484fd5b503461050b57602036600319011261050b5760209060ff906040906001600160a01b03611abf611fed565b168152601884522054166040519015158152f35b503461050b57602036600319011261050b576113a56004356124dd565b503461050b57602036600319011261050b576020906040906001600160a01b03611b18611fed565b168152600583522054604051908152f35b503461050b57602036600319011261050b5780611b44611fed565b6040516342f9577960e11b8152602081600481867f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015610a4f57611ba59184916113a857506001600160a01b03163314612448565b600c546001600160a01b031690611bbd821515612480565b813b15611c0d57604051635efcc08b60e11b81526001600160a01b0390911660048201529082908290602490829084905af1801561048c57611bfc5750f35b81611c0691612049565b61050b5780f35b5050fd5b503461050b57602036600319011261050b576020906040906001600160a01b03611c39611fed565b168152600383522054604051908152f35b5034610c78576040366003190112610c7857611c64611fed565b90611c6d612003565b6040516342f9577960e11b81529092906020816004815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015611e9657611cd1915f916113a857506001600160a01b03163314612448565b600c5490611ce86001600160a01b03831615612480565b6001600160a01b0381166001600160a01b0319929092168217600c55813b15610c7857604051635efcc08b60e11b81526001600160a01b0390941660048501525f8460248183865af18015611e9657611e80575b6040516370a0823160e01b81523060048201529293508392907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602083602481845afa928315610f30578593611e49575b5082611da0578480f35b60405163095ea7b360e01b81526001600160a01b03929092166004830152602482018390526020908290604490829088905af1801561097757611e2c575b50813b15611c0d57829160248392604051948593849263b6b55f2560e01b845260048401525af1801561048c57611e17575b8080808480f35b81611e2191612049565b61050b57805f611e10565b611e449060203d602011610a4857610a3b8183612049565b611dde565b945091506020843d602011611e78575b81611e6660209383612049565b81010312610c7857849351915f611d96565b3d9150611e59565b9150915f611e8d91612049565b5f908290611d3c565b6040513d5f823e3d90fd5b34610c78576040366003190112610c78576020610617611ebf611fed565b611ec7612003565b90612249565b34610c78575f366003190112610c78576020600a54604051908152f35b34610c78576040366003190112610c78576020610617611f08611fed565b6024359061210a565b34610c78576040366003190112610c78576001600160a01b03611f32611fed565b165f52601960205260405f206024355f526020526040805f206001815491015482519182526020820152f35b34610c78575f366003190112610c78576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610c78576040366003190112610c78576040906001600160a01b03611fc6611fed565b165f52601d602052815f206024355f52602052815f20600181549101549082526020820152f35b600435906001600160a01b0382168203610c7857565b602435906001600160a01b0382168203610c7857565b6040810190811067ffffffffffffffff82111761203557604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761203557604052565b6040906003190112610c78576004359060243590565b600d5481101561209957600d5f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b919082039182116120ba57565b634e487b7160e01b5f52601160045260245ffd5b81156120d8570490565b634e487b7160e01b5f52601260045260245ffd5b906040516120f981612019565b602060018294805484520154910152565b6001600160a01b03165f818152601a602052604090205491821561221457815f52601960205260405f20925f19810190811193846120ba57815f526020528160405f2054111561220d57825f52601960205260405f205f80526020528160405f205411612205575f936120ba5791905b8383116121875750505090565b61219d61219485856120ad565b60011c846120ad565b93825f52601960205260405f20855f526020526121bc60405f206120ec565b518281036121cc57505050505090565b918093949592105f146121e35750925b919061217a565b93505f198101908111156121dc57634e487b7160e01b5f52601160045260245ffd5b505050505f90565b9250505090565b5050505f90565b5f1981146120ba5760010190565b919082018092116120ba57565b818102929181159184041417156120ba57565b6001600160a01b038181165f81815260076020908152604080832094871680845294825280832054848452601d8352818420848052835281842054868552601a90935292205493959293909291901561241f5782816122ae941190821802189061210a565b92805f52601a60205260405f20545f198101908082116120ba575f9582612357575b505092612347670de0b6b3a76400009383612354979661234d955f52601960205260405f20905f5260205261230760405f206120ec565b906123226020612318845188612a58565b50930151956129a9565b925f52600860205260405f20905f5260205260405f20548181119082180218906120ad565b90612236565b0490612229565b90565b9692969391906001190181811196875b6120ba5781861161240457885f52601960205260405f20865f5260205261239060405f206120ec565b895f52601960205260405f209160018801928389116120ba5761234d6123f793670de0b6b3a7640000926123fd965f526020526123478c60206123ed6123d860405f206120ec565b926123e4865182612a58565b50935190612a58565b50930151926120ad565b9561221b565b9487612367565b93979396509193509050612347670de0b6b3a76400006122d0565b5050505050505f90565b90816020910312610c7857516001600160a01b0381168103610c785790565b1561244f57565b60405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b6044820152606490fd5b1561248757565b60405162461bcd60e51b81526020600482015260166024820152757374616b696e6720616c72656164792065786973747360501b6044820152606490fd5b90816020910312610c7857518015158103610c785790565b5f905f91335f52600b60205260405f2054821461278a575b6001601f5403610c78576002601f558261250d6132be565b61251983600a546120ad565b600a55335f52600b60205260405f206125338482546120ad565b9055600c546001600160a01b031680612745575b5061257383337f0000000000000000000000000000000000000000000000000000000000000000612eb8565b156127315733815260096020526040812054830361050b573381526009602052604081208190557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104975760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561048c5790829161271c575b50505b338152600160205261261b604082205482546120ad565b8155338152600b602052612642604082205433835260016020528060408420558254612229565b8155338152600160205261265a604082205433612f4b565b61266261301b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b1561049757604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c57612707575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601f55565b612712828092612049565b61050b57806126cd565b8161272691612049565b61050b57805f612601565b338152600960205260408120549250612604565b803b15610c78575f8091602460405180948193632e1a7d4d60e01b83528960048401525af18015611e965715612547576127829192505f90612049565b5f905f612547565b9150335f52600960205260405f2054916124f5565b80518210156120995760209160051b010190565b601c54908115612899575f19820191821191826120ba57805f52601b6020528160405f20541115612893575f8052601b6020527f584f46c60af19681376031579adb04a2416e54ee5505351c2a8435e3766026ea548210612214575f926120ba57905b82821161282257505090565b61283861282f84846120ad565b60011c836120ad565b92835f52601b60205261284d60405f206120ec565b5182810361285c575050505090565b9180939492105f146128715750915b90612816565b92505f1981019081111561286b57634e487b7160e01b5f52601160045260245ffd5b91505090565b50505f90565b6001600160a01b03165f818152600460205260409020544210156128ea5780612354915f5260046020526128d74260405f20546120ad565b905f52600360205260405f205490612236565b505f90565b6040516342f9577960e11b81526020816004815f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015611e9657612950915f916113a857506001600160a01b03163314612448565b600c546001600160a01b031680156129a657803b15610c78575f8091604460405180948193639faceb1b60e01b83523060048401528160248401525af18015611e965761299a5750565b5f6129a491612049565b565b50565b5f54908115612a3c576001600160a01b03165f81815260066020908152604080832054600480845282852054600585529290942054939092529092612a089290916128d7918082188183100218904281811191811891909102186120ad565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156120ba5761235492612a36916120ce565b90612229565b6001600160a01b03165f90815260066020526040902054919050565b6001600160a01b03165f818152601e60205260409020549092918115612bae57835f52601d60205260405f20915f19810190811192836120ba57815f526020528160405f20541115612b8657845f52601d60205260405f205f80526020528160405f205411612b7b575f926120ba57905b828211612af65750505f928352601d60209081526040808520928552919052909120600181015490549091565b612b0661282f84849795976120ad565b835f52601d60205260405f20815f52602052612b2460405f206120ec565b9586518381145f14612b40575050505050506020820151915190565b839495975092909192105f14612b595750915b90612ac9565b92505f19810190811115612b5357634e487b7160e01b5f52601160045260245ffd5b50505090505f905f90565b5f948552601d6020908152604080872092875291905290932060018101549054909350919050565b505090505f905f90565b9060018060a01b038216805f52600560205260405f205492815f52600660205260405f205491601c54908115612c36575f52600360205260405f205415612d4557612c02856127b3565b5f19820191908183116120ba5782612c9d575b50505f52601b602052612c2a60405f206120ec565b60208101908151612c3d575b5050509190565b8295612c939492612c8392612c6f612c8a9660018060a01b03165f52600460205260405f205442811090421802421890565b905191519180841184821802189089613314565b5090612229565b9283429161319d565b42915f8080612c36565b9491906001190181811195865b6120ba57818111612d3957805f52601b602052612cc960405f206120ec565b602081018051612ce5575b5050612cdf9061221b565b86612caa565b90919860018a0190818b116120ba57612cdf93612d2593612d1f935f52601b602052612d1360405f206120ec565b5191519051918a613314565b97612229565b95612d3181888861319d565b97905f612cd4565b50509093505f80612c15565b5050909150904290565b919060018060a01b038316805f52600560205260405f205493815f52600660205260405f205492601c54928315612e71575f52600360205260405f205415612e6657612d9a866127b3565b905f1984019384116120ba57838110908418028084189303612dbd575050509190565b5f19830192831194919290855b6120ba57818111612e5957805f52601b602052612de960405f206120ec565b602081018051612e05575b5050612dff9061221b565b85612dca565b9091976001890190818a116120ba57612dff93612e4593612e3f935f52601b602052612e3360405f206120ec565b51915190519189613314565b96612229565b94612e5181878761319d565b96905f612df4565b50509250505f8080612c36565b505050909150904290565b505050509190565b3d15612eb3573d9067ffffffffffffffff82116120355760405191612ea8601f8201601f191660200184612049565b82523d5f602084013e565b606090565b919091803b15610c785760405163a9059cbb60e01b602082019081526001600160a01b039490941660248201526044808201939093529182525f9283928390612f02606482612049565b51925af1612f0e612e79565b81612f1c575b5015610c7857565b8051801592508215612f31575b50505f612f14565b612f4492506020809183010191016124c5565b5f80612f29565b6001600160a01b03165f818152601a60205260409020549081151580612ff2575b15612f97575f52601960205260405f20905f1981019081116120ba575f52602052600160405f200155565b9091612fd99060405190612faa82612019565b4282526020820152825f52601960205260405f20845f5260205260405f20906020600191805184550151910155565b600182018092116120ba575f52601a60205260405f2055565b50805f52601960205260405f205f1983018381116120ba575f526020524260405f205414612f6c565b601c5480151580613092575b1561304b575f54905f1981019081116120ba575f52601b602052600160405f200155565b6130825f546040519061305d82612019565b4282526020820152825f52601b60205260405f20906020600191805184550151910155565b600181018091116120ba57601c55565b505f1981018181116120ba575f52601b6020524260405f205414613027565b919060018060a01b03831690815f52600560205260405f205493825f52600660205260405f205492601c54908115612e71575f52600360205260405f205415612e66576130fd866127b3565b925f1982019182116120ba578181109082180218915b828110613121575050509190565b805f52601b60205261313560405f206120ec565b60208101805161314a575b5050600101613113565b90919660018801908189116120ba5760019361318993613183935f52601b60205261317760405f206120ec565b51915190519187613314565b95612229565b9361319581868561319d565b95905f613140565b6001600160a01b03165f818152601e6020526040902054919282151580613245575b156131eb57505f52601d60205260405f20905f1981019081116120ba575f52602052600160405f200155565b61322c919293604051916131fe83612019565b82526020820152825f52601d60205260405f20845f5260205260405f20906020600191805184550151910155565b600182018092116120ba575f52601e60205260405f2055565b50815f52601d60205260405f205f1984018481116120ba575f526020528060405f2054146131bf565b90813b15610c78576040516323b872dd60e01b602082019081526001600160a01b0392831660248301529190931660448401526064808401949094529282525f9283928390612f02608482612049565b600d545f5b8181106132ce575050565b806132da600192612081565b838060a01b0391549060031b1c166132f181612bb8565b825f93929352600660205260405f20915f52600560205260405f205555016132c3565b6001600160a01b03165f818152600460205260409020548286188387110290921895946133659391926128d792808318928110929092029091188082188183100218908088189088110287186120ad565b90670de0b6b3a7640000820291808304670de0b6b3a764000014901517156120ba57613390916120ce565b919056fea264697066735822122045575e00b1ee184ccf22d57f3cba5c2cbc6989d7c0ca3050ed9db5fbce5e22ea64736f6c634300081c003300000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba8f46e97599a038c7c1b63f6e4c0357d65ab50a0000000000000000000000000c0c11d859dbd097cef8e965c691bcc20a64f68b000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301316ddf14611fa25750806303fbf83a14611f5e5780630cdfebfa14611f11578063115c6f3914611eea57806318160ddd14611ecd578063211dc32d14611ea157806321cc096814611c4a578063221ca18c14611c115780632619582614611b295780632ce9aead14611af05780632e1a7d4d14611ad35780632f85074a14611a9457806331279d3d146117435780633a4b66f1146116fe5780633ca068b6146116ab57806346c96aac146116665780634cf088d91461163d5780634d5ce038146115fe578063597a8d6f146115c65780635a45d052146115a0578063638634ee1461156157806363fb415b1461152857806368fcee1a146114da5780636fcfff45146114a157806370a0823114610ab057806376f4be3614611482578063770f85711461143d578063853828b6146114175780638dd598fb146113d25780639418f939146112b257806399bcc0521461128e5780639ce43f9014611255578063a0dc275814611237578063a495e5b5146111e4578063aa479652146111ab578063b66503cf14610c84578063c6f678bd14610b19578063d294f09314610ae9578063d35e254414610ab0578063d7da4bb014610a93578063da09d19d14610a5a578063e2bbb1581461067e578063e57482131461065b578063e68863961461063d578063e8111a121461061f578063f1229777146105f3578063f301af42146105af578063f7412baf1461057b578063fc97a30314610542578063fd3140981461050e5763fdb483c71461024f575f80fd5b3461050b5761025d3661206b565b906001601f5403610488576002601f55816102766132be565b61028282600a546120ad565b600a55338452600b6020526040842061029c8382546120ad565b9055600c5484906001600160a01b0316806104c4575b50506102df82337f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e33612eb8565b156104b05733835260096020526040832054820361048857338352600960205260408320839055827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104975760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561048c5761049b575b50505b3383526001602052610385604084205484546120ad565b8355338352600b6020526103ac604084205433855260016020528060408620558454612229565b835533835260016020526103c4604084205433612f4b565b6103cc61301b565b827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b1561049757604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c57610473575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601f5580f35b8161047d91612049565b61048857825f610438565b8280fd5b6040513d84823e3d90fd5b5080fd5b816104a591612049565b61048857825f61036b565b33835260096020526040832054915061036e565b803b1561049757818091602460405180948193632e1a7d4d60e01b83528960048401525af1801561048c57156102b257816104fe91612049565b61050957835f6102b2565b835b80fd5b503461050b57604036600319011261050b57604061053661052d611fed565b60243590612a58565b82519182526020820152f35b503461050b57602036600319011261050b576020906040906001600160a01b0361056a611fed565b168152600983522054604051908152f35b503461050b57602036600319011261050b57604080916004358152601b602052206001815491015482519182526020820152f35b503461050b57602036600319011261050b5760043590600d5482101561050b5760206105da83612081565b905460405160039290921b1c6001600160a01b03168152f35b503461050b57602036600319011261050b576020610617610612611fed565b6129a9565b604051908152f35b503461050b578060031936011261050b576020601c54604051908152f35b503461050b578060031936011261050b576020600d54604051908152f35b503461050b578060031936011261050b57602060ff600254166040519015158152f35b503461050b5761068d3661206b565b906001601f5403610488576002601f55818115610509576106ac6132be565b837f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e336106da8430338461326e565b600c546001600160a01b03169081610996575b5050506106fc82600a54612229565b600a55338452600b60205260408420610716838254612229565b905515610982576040516331a9108f60e11b8152600481018390526020816024817f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165afa908115610977578491610948575b50336001600160a01b03909116036104885733835260096020526040832054156108b8575b338352600960205281604084205403610488575b33835260016020526107c1604084205484546120ad565b8355338352600b6020526107f4604084205433855260016020528060408620556107ec818654612229565b855533612f4b565b6107fc61301b565b827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104975760405163530e389d60e11b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c576108a3575b505060405191825260208201527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1560403392a26001601f5580f35b816108ad91612049565b61048857825f610868565b338352600960205260408320829055827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104975760405163698473e360e01b8152600481018590523360248201529082908290604490829084905af1801561048c57610933575b5050610796565b8161093d91612049565b61048857825f61092c565b61096a915060203d602011610970575b6109628183612049565b810190612429565b5f610771565b503d610958565b6040513d86823e3d90fd5b3383526009602052604083205491506107aa565b60405163095ea7b360e01b81526001600160a01b038381166004830152602482018790529091602091839160449183918891165af18015610a4f57610a22575b50803b156104975781809160246040518094819363b6b55f2560e01b83528960048401525af1801561048c57610a0d575b806106ed565b81610a1791612049565b61050957835f610a07565b610a439060203d602011610a48575b610a3b8183612049565b8101906124c5565b6109d6565b503d610a31565b6040513d85823e3d90fd5b503461050b57602036600319011261050b576020906040906001600160a01b03610a82611fed565b168152600483522054604051908152f35b503461050b578060031936011261050b5760209054604051908152f35b503461050b57602036600319011261050b576020906040906001600160a01b03610ad8611fed565b168152600b83522054604051908152f35b503461050b578060031936011261050b576001601f540361050b576002601f55610b116128ef565b6001601f5580f35b503461050b57602036600319011261050b576040516370a0823160e01b8152336004828101919091527f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e33919035836001600160a01b038416602084602481845afa93841561048c578294610c49575b506001601f5403610497576002601f55829484156104885784610bb691610bad6132be565b3090339061326e565b600c546001600160a01b03169081610bd7575050506106fc82600a54612229565b60405163095ea7b360e01b81526001600160a01b038316600482015260248101869052906020908290604490829087905af18015610a4f57610a225750803b156104975781809160246040518094819363b6b55f2560e01b83528960048401525af1801561048c57610a0d57806106ed565b915092506020813d602011610c7c575b81610c6660209383612049565b81010312610c7857849051925f610b88565b5f80fd5b3d9150610c59565b503461050b57604036600319011261050b57610c9e611fed565b6024356001601f5403610488576002601f556001600160a01b0382811692907f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e3316831461050957811561050957828452601760205260ff60408520541615611093575b8284526003602052604084205415610fa0575b610d1d81612bb8565b848652600660205260408620908587526005602052604087205555828452600460205260408420544210155f14610f3b57610d5a8230338461326e565b828452600360205262093a80820460408520555b8284526003602052604084205415610509576040516370a0823160e01b8152306004820152602081602481875afa908115610f30578591610efe575b50838552600360205262093a806040862054910410610eb95762093a804201804211610ea55783855260046020526040852055828452601760205260ff60408520541615610e28575b506040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a36001601f5580f35b828452601760205260408420805460ff19166001179055600d5468010000000000000000811015610e915790610e67826001610e8b9401600d55612081565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f610df3565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606490fd5b90506020813d602011610f28575b81610f1960209383612049565b81010312610c7857515f610daa565b3d9150610f0c565b6040513d87823e3d90fd5b8284526004602052610f67610f544260408720546120ad565b8486526003602052604086205490612236565b80831115610f9c57610f8962093a8091610f838530338761326e565b84612229565b0483855260036020526040852055610d6e565b8480fd5b828452601e602052604084205480151580611056575b15610ffa57838552601d60205260408520905f198101908111610fe6578552602052836001604082200155610d14565b634e487b7160e01b86526011600452602486fd5b61103960405161100981612019565b428152866020820152858752601d6020526040872083885260205260408720906020600191805184550151910155565b60018101809111610ea557838552601e6020526040852055610d14565b50838552601d602052604085205f19820182811161107f57865260205242604086205414610fb6565b634e487b7160e01b87526011600452602487fd5b604051633af32abf60e01b8152600481018490526020816024817f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b03165afa908115610f3057859161118c575b501561113c576010600d5410610d015760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608490fd5b6111a5915060203d602011610a4857610a3b8183612049565b5f6110e7565b503461050b57602036600319011261050b576020906040906001600160a01b036111d3611fed565b168152601e83522054604051908152f35b503461050b57604036600319011261050b576040611200611fed565b91611209612003565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461050b578060031936011261050b57602060405162093a808152f35b503461050b57602036600319011261050b576020906040906001600160a01b0361127d611fed565b168152600683522054604051908152f35b503461050b57602036600319011261050b5760206106176112ad611fed565b61289f565b503461050b57606036600319011261050b576004356112cf612003565b6044356001600160a01b03811692909190838303610f9c576040516342f9577960e11b8152602081600481897f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165af180156113c7576113499187916113a8575b506001600160a01b03163314612448565b61135281612081565b90546001600160a01b039384169360039290921b1c16829003610f9c576113a593610e6792865260176020526040862060ff1981541690558552601760205260408520600160ff19825416179055612081565b80f35b6113c1915060203d602011610970576109628183612049565b5f611338565b6040513d88823e3d90fd5b503461050b578060031936011261050b576040517f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03168152602090f35b503461050b578060031936011261050b57338152600b6020526113a560408220546124dd565b503461050b578060031936011261050b576040517f000000000000000000000000ba8f46e97599a038c7c1b63f6e4c0357d65ab50a6001600160a01b03168152602090f35b503461050b57602036600319011261050b5760206106176004356127b3565b503461050b57602036600319011261050b576020906040906001600160a01b036114c9611fed565b168152601a83522054604051908152f35b503461050b57604036600319011261050b576114f4611fed565b61150060243582612d4f565b909160018060a01b031690818452600660205260408420918452600560205260408420555580f35b503461050b57602036600319011261050b576020906040906001600160a01b03611550611fed565b168152600183522054604051908152f35b503461050b57602036600319011261050b576020610617611580611fed565b60018060a01b03165f52600460205260405f205442811090421802421890565b503461050b57604036600319011261050b576115ba611fed565b611500602435826130b1565b503461050b57602036600319011261050b57600435600881101561049757600e01546040516001600160a01b03909116815260209150f35b503461050b57602036600319011261050b5760209060ff906040906001600160a01b03611629611fed565b168152601784522054166040519015158152f35b503461050b578060031936011261050b57600c546040516001600160a01b039091168152602090f35b503461050b578060031936011261050b576040517f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b03168152602090f35b503461050b57604036600319011261050b5760406116c7611fed565b916116d0612003565b9260018060a01b031681526008602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461050b578060031936011261050b576040517f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e336001600160a01b03168152602090f35b503461050b57604036600319011261050b5761175d611fed565b60243567ffffffffffffffff811161048857366023820112156104885780600401359067ffffffffffffffff8211611a80578160051b90604051926117a56020840185612049565b83526024602084019282010190368211611a7c57602401915b818310611a58575050506001601f5403610488576001600160a01b038216903382148015611a26575b15610509576001601f55837f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b15610497578180916024604051809481936363453ae160e01b83523060048401525af1801561048c57611a11575b50506002601f55835b81518110156119b15760019061187d6001600160a01b03611876838661279f565b5116612bb8565b838060a01b0361188d848761279f565b5116885260066020526040882090848060a01b036118ab858861279f565b5116895260056020526040892055556118d585838060a01b036118ce848761279f565b5116612249565b828060a01b036118e5838661279f565b51168752600760205260408720855f5260205260405f20429055828060a01b0361190f838661279f565b5116875260066020526040872054838060a01b0361192d848761279f565b51168852600860205260408820865f5260205260405f205580611990575b828060a01b0361195b838661279f565b5116906040519081527f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc960203392a301611855565b6119ac8187858060a01b036119a5868961279f565b5116612eb8565b61194b565b84611a09858580845260016020526119ce604085205485546120ad565b8455808452600b6020526119f5604085205482865260016020528060408720558554612229565b845583526001602052604083205490612f4b565b610b1161301b565b81611a1b91612049565b61050957835f61184c565b50337f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316146117e7565b82356001600160a01b0381168103611a78578152602092830192016117be565b8680fd5b8580fd5b634e487b7160e01b84526041600452602484fd5b503461050b57602036600319011261050b5760209060ff906040906001600160a01b03611abf611fed565b168152601884522054166040519015158152f35b503461050b57602036600319011261050b576113a56004356124dd565b503461050b57602036600319011261050b576020906040906001600160a01b03611b18611fed565b168152600583522054604051908152f35b503461050b57602036600319011261050b5780611b44611fed565b6040516342f9577960e11b8152602081600481867f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165af18015610a4f57611ba59184916113a857506001600160a01b03163314612448565b600c546001600160a01b031690611bbd821515612480565b813b15611c0d57604051635efcc08b60e11b81526001600160a01b0390911660048201529082908290602490829084905af1801561048c57611bfc5750f35b81611c0691612049565b61050b5780f35b5050fd5b503461050b57602036600319011261050b576020906040906001600160a01b03611c39611fed565b168152600383522054604051908152f35b5034610c78576040366003190112610c7857611c64611fed565b90611c6d612003565b6040516342f9577960e11b81529092906020816004815f7f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165af18015611e9657611cd1915f916113a857506001600160a01b03163314612448565b600c5490611ce86001600160a01b03831615612480565b6001600160a01b0381166001600160a01b0319929092168217600c55813b15610c7857604051635efcc08b60e11b81526001600160a01b0390941660048501525f8460248183865af18015611e9657611e80575b6040516370a0823160e01b81523060048201529293508392907f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e336001600160a01b0316602083602481845afa928315610f30578593611e49575b5082611da0578480f35b60405163095ea7b360e01b81526001600160a01b03929092166004830152602482018390526020908290604490829088905af1801561097757611e2c575b50813b15611c0d57829160248392604051948593849263b6b55f2560e01b845260048401525af1801561048c57611e17575b8080808480f35b81611e2191612049565b61050b57805f611e10565b611e449060203d602011610a4857610a3b8183612049565b611dde565b945091506020843d602011611e78575b81611e6660209383612049565b81010312610c7857849351915f611d96565b3d9150611e59565b9150915f611e8d91612049565b5f908290611d3c565b6040513d5f823e3d90fd5b34610c78576040366003190112610c78576020610617611ebf611fed565b611ec7612003565b90612249565b34610c78575f366003190112610c78576020600a54604051908152f35b34610c78576040366003190112610c78576020610617611f08611fed565b6024359061210a565b34610c78576040366003190112610c78576001600160a01b03611f32611fed565b165f52601960205260405f206024355f526020526040805f206001815491015482519182526020820152f35b34610c78575f366003190112610c78576040517f0000000000000000000000000c0c11d859dbd097cef8e965c691bcc20a64f68b6001600160a01b03168152602090f35b34610c78576040366003190112610c78576040906001600160a01b03611fc6611fed565b165f52601d602052815f206024355f52602052815f20600181549101549082526020820152f35b600435906001600160a01b0382168203610c7857565b602435906001600160a01b0382168203610c7857565b6040810190811067ffffffffffffffff82111761203557604052565b634e487b7160e01b5f52604160045260245ffd5b90601f8019910116810190811067ffffffffffffffff82111761203557604052565b6040906003190112610c78576004359060243590565b600d5481101561209957600d5f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b919082039182116120ba57565b634e487b7160e01b5f52601160045260245ffd5b81156120d8570490565b634e487b7160e01b5f52601260045260245ffd5b906040516120f981612019565b602060018294805484520154910152565b6001600160a01b03165f818152601a602052604090205491821561221457815f52601960205260405f20925f19810190811193846120ba57815f526020528160405f2054111561220d57825f52601960205260405f205f80526020528160405f205411612205575f936120ba5791905b8383116121875750505090565b61219d61219485856120ad565b60011c846120ad565b93825f52601960205260405f20855f526020526121bc60405f206120ec565b518281036121cc57505050505090565b918093949592105f146121e35750925b919061217a565b93505f198101908111156121dc57634e487b7160e01b5f52601160045260245ffd5b505050505f90565b9250505090565b5050505f90565b5f1981146120ba5760010190565b919082018092116120ba57565b818102929181159184041417156120ba57565b6001600160a01b038181165f81815260076020908152604080832094871680845294825280832054848452601d8352818420848052835281842054868552601a90935292205493959293909291901561241f5782816122ae941190821802189061210a565b92805f52601a60205260405f20545f198101908082116120ba575f9582612357575b505092612347670de0b6b3a76400009383612354979661234d955f52601960205260405f20905f5260205261230760405f206120ec565b906123226020612318845188612a58565b50930151956129a9565b925f52600860205260405f20905f5260205260405f20548181119082180218906120ad565b90612236565b0490612229565b90565b9692969391906001190181811196875b6120ba5781861161240457885f52601960205260405f20865f5260205261239060405f206120ec565b895f52601960205260405f209160018801928389116120ba5761234d6123f793670de0b6b3a7640000926123fd965f526020526123478c60206123ed6123d860405f206120ec565b926123e4865182612a58565b50935190612a58565b50930151926120ad565b9561221b565b9487612367565b93979396509193509050612347670de0b6b3a76400006122d0565b5050505050505f90565b90816020910312610c7857516001600160a01b0381168103610c785790565b1561244f57565b60405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b6044820152606490fd5b1561248757565b60405162461bcd60e51b81526020600482015260166024820152757374616b696e6720616c72656164792065786973747360501b6044820152606490fd5b90816020910312610c7857518015158103610c785790565b5f905f91335f52600b60205260405f2054821461278a575b6001601f5403610c78576002601f558261250d6132be565b61251983600a546120ad565b600a55335f52600b60205260405f206125338482546120ad565b9055600c546001600160a01b031680612745575b5061257383337f00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e33612eb8565b156127315733815260096020526040812054830361050b573381526009602052604081208190557f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104975760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561048c5790829161271c575b50505b338152600160205261261b604082205482546120ad565b8155338152600b602052612642604082205433835260016020528060408420558254612229565b8155338152600160205261265a604082205433612f4b565b61266261301b565b7f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b1561049757604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561048c57612707575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601f55565b612712828092612049565b61050b57806126cd565b8161272691612049565b61050b57805f612601565b338152600960205260408120549250612604565b803b15610c78575f8091602460405180948193632e1a7d4d60e01b83528960048401525af18015611e965715612547576127829192505f90612049565b5f905f612547565b9150335f52600960205260405f2054916124f5565b80518210156120995760209160051b010190565b601c54908115612899575f19820191821191826120ba57805f52601b6020528160405f20541115612893575f8052601b6020527f584f46c60af19681376031579adb04a2416e54ee5505351c2a8435e3766026ea548210612214575f926120ba57905b82821161282257505090565b61283861282f84846120ad565b60011c836120ad565b92835f52601b60205261284d60405f206120ec565b5182810361285c575050505090565b9180939492105f146128715750915b90612816565b92505f1981019081111561286b57634e487b7160e01b5f52601160045260245ffd5b91505090565b50505f90565b6001600160a01b03165f818152600460205260409020544210156128ea5780612354915f5260046020526128d74260405f20546120ad565b905f52600360205260405f205490612236565b505f90565b6040516342f9577960e11b81526020816004815f7f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165af18015611e9657612950915f916113a857506001600160a01b03163314612448565b600c546001600160a01b031680156129a657803b15610c78575f8091604460405180948193639faceb1b60e01b83523060048401528160248401525af18015611e965761299a5750565b5f6129a491612049565b565b50565b5f54908115612a3c576001600160a01b03165f81815260066020908152604080832054600480845282852054600585529290942054939092529092612a089290916128d7918082188183100218904281811191811891909102186120ad565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156120ba5761235492612a36916120ce565b90612229565b6001600160a01b03165f90815260066020526040902054919050565b6001600160a01b03165f818152601e60205260409020549092918115612bae57835f52601d60205260405f20915f19810190811192836120ba57815f526020528160405f20541115612b8657845f52601d60205260405f205f80526020528160405f205411612b7b575f926120ba57905b828211612af65750505f928352601d60209081526040808520928552919052909120600181015490549091565b612b0661282f84849795976120ad565b835f52601d60205260405f20815f52602052612b2460405f206120ec565b9586518381145f14612b40575050505050506020820151915190565b839495975092909192105f14612b595750915b90612ac9565b92505f19810190811115612b5357634e487b7160e01b5f52601160045260245ffd5b50505090505f905f90565b5f948552601d6020908152604080872092875291905290932060018101549054909350919050565b505090505f905f90565b9060018060a01b038216805f52600560205260405f205492815f52600660205260405f205491601c54908115612c36575f52600360205260405f205415612d4557612c02856127b3565b5f19820191908183116120ba5782612c9d575b50505f52601b602052612c2a60405f206120ec565b60208101908151612c3d575b5050509190565b8295612c939492612c8392612c6f612c8a9660018060a01b03165f52600460205260405f205442811090421802421890565b905191519180841184821802189089613314565b5090612229565b9283429161319d565b42915f8080612c36565b9491906001190181811195865b6120ba57818111612d3957805f52601b602052612cc960405f206120ec565b602081018051612ce5575b5050612cdf9061221b565b86612caa565b90919860018a0190818b116120ba57612cdf93612d2593612d1f935f52601b602052612d1360405f206120ec565b5191519051918a613314565b97612229565b95612d3181888861319d565b97905f612cd4565b50509093505f80612c15565b5050909150904290565b919060018060a01b038316805f52600560205260405f205493815f52600660205260405f205492601c54928315612e71575f52600360205260405f205415612e6657612d9a866127b3565b905f1984019384116120ba57838110908418028084189303612dbd575050509190565b5f19830192831194919290855b6120ba57818111612e5957805f52601b602052612de960405f206120ec565b602081018051612e05575b5050612dff9061221b565b85612dca565b9091976001890190818a116120ba57612dff93612e4593612e3f935f52601b602052612e3360405f206120ec565b51915190519189613314565b96612229565b94612e5181878761319d565b96905f612df4565b50509250505f8080612c36565b505050909150904290565b505050509190565b3d15612eb3573d9067ffffffffffffffff82116120355760405191612ea8601f8201601f191660200184612049565b82523d5f602084013e565b606090565b919091803b15610c785760405163a9059cbb60e01b602082019081526001600160a01b039490941660248201526044808201939093529182525f9283928390612f02606482612049565b51925af1612f0e612e79565b81612f1c575b5015610c7857565b8051801592508215612f31575b50505f612f14565b612f4492506020809183010191016124c5565b5f80612f29565b6001600160a01b03165f818152601a60205260409020549081151580612ff2575b15612f97575f52601960205260405f20905f1981019081116120ba575f52602052600160405f200155565b9091612fd99060405190612faa82612019565b4282526020820152825f52601960205260405f20845f5260205260405f20906020600191805184550151910155565b600182018092116120ba575f52601a60205260405f2055565b50805f52601960205260405f205f1983018381116120ba575f526020524260405f205414612f6c565b601c5480151580613092575b1561304b575f54905f1981019081116120ba575f52601b602052600160405f200155565b6130825f546040519061305d82612019565b4282526020820152825f52601b60205260405f20906020600191805184550151910155565b600181018091116120ba57601c55565b505f1981018181116120ba575f52601b6020524260405f205414613027565b919060018060a01b03831690815f52600560205260405f205493825f52600660205260405f205492601c54908115612e71575f52600360205260405f205415612e66576130fd866127b3565b925f1982019182116120ba578181109082180218915b828110613121575050509190565b805f52601b60205261313560405f206120ec565b60208101805161314a575b5050600101613113565b90919660018801908189116120ba5760019361318993613183935f52601b60205261317760405f206120ec565b51915190519187613314565b95612229565b9361319581868561319d565b95905f613140565b6001600160a01b03165f818152601e6020526040902054919282151580613245575b156131eb57505f52601d60205260405f20905f1981019081116120ba575f52602052600160405f200155565b61322c919293604051916131fe83612019565b82526020820152825f52601d60205260405f20845f5260205260405f20906020600191805184550151910155565b600182018092116120ba575f52601e60205260405f2055565b50815f52601d60205260405f205f1984018481116120ba575f526020528060405f2054146131bf565b90813b15610c78576040516323b872dd60e01b602082019081526001600160a01b0392831660248301529190931660448401526064808401949094529282525f9283928390612f02608482612049565b600d545f5b8181106132ce575050565b806132da600192612081565b838060a01b0391549060031b1c166132f181612bb8565b825f93929352600660205260405f20915f52600560205260405f205555016132c3565b6001600160a01b03165f818152600460205260409020548286188387110290921895946133659391926128d792808318928110929092029091188082188183100218908088189088110287186120ad565b90670de0b6b3a7640000820291808304670de0b6b3a764000014901517156120ba57613390916120ce565b919056fea264697066735822122045575e00b1ee184ccf22d57f3cba5c2cbc6989d7c0ca3050ed9db5fbce5e22ea64736f6c634300081c0033

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

00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ba8f46e97599a038c7c1b63f6e4c0357d65ab50a0000000000000000000000000c0c11d859dbd097cef8e965c691bcc20a64f68b000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb00000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db

-----Decoded View---------------
Arg [0] : _stake (address): 0x70ac2feeB9ab4417591a97AD2607DD0E87bb3e33
Arg [1] : _staking (address): 0x0000000000000000000000000000000000000000
Arg [2] : _internal_bribe (address): 0xbA8F46E97599a038C7c1b63f6E4C0357d65AB50A
Arg [3] : _external_bribe (address): 0x0C0C11d859DBd097ceF8e965C691bCc20a64F68B
Arg [4] : __ve (address): 0xdB9A1bdc443dd11366b8a6dc8038144eCc4D4E23
Arg [5] : _voter (address): 0xF3113E4F80c84935E576CFD75F4423E9B911908A
Arg [6] : _forPair (bool): True
Arg [7] : _allowedRewardTokens (address[]): 0x8fF0dd9f9C40a0d76eF1BcFAF5f98c1610c74Bd8,0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb,0x28245AB01298eaEf7933bC90d35Bd9DbCA5C89DB

-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 00000000000000000000000070ac2feeb9ab4417591a97ad2607dd0e87bb3e33
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000ba8f46e97599a038c7c1b63f6e4c0357d65ab50a
Arg [3] : 0000000000000000000000000c0c11d859dbd097cef8e965c691bcc20a64f68b
Arg [4] : 000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23
Arg [5] : 000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [9] : 0000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8
Arg [10] : 000000000000000000000000b8ce59fc3717ada4c02eadf9682a9e934f625ebb
Arg [11] : 00000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db


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
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.