HYPE Price: $22.17 (+0.15%)
 

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
Block
From
To
Withdraw164365352025-10-14 5:34:03104 days ago1760420043IN
0x1f57A627...2E4082761
0 HYPE0.000113270.4322065
Withdraw159508492025-10-08 16:49:30109 days ago1759942170IN
0x1f57A627...2E4082761
0 HYPE0.00049111.87370026
Withdraw147790302025-09-25 8:37:54122 days ago1758789474IN
0x1f57A627...2E4082761
0 HYPE0.000112640.42978807
Withdraw146713002025-09-24 3:11:50124 days ago1758683510IN
0x1f57A627...2E4082761
0 HYPE0.0057301521.86187898
Withdraw146648372025-09-24 1:25:53124 days ago1758677153IN
0x1f57A627...2E4082761
0 HYPE0.000967923.69285783
Withdraw145832712025-09-23 3:08:43125 days ago1758596923IN
0x1f57A627...2E4082761
0 HYPE0.001162744.43615311
Withdraw144850192025-09-22 0:18:01126 days ago1758500281IN
0x1f57A627...2E4082761
0 HYPE0.000033840.12914372
Withdraw143041882025-09-19 22:52:17128 days ago1758322337IN
0x1f57A627...2E4082761
0 HYPE0.00003170.1209771
Withdraw142710532025-09-19 13:49:05128 days ago1758289745IN
0x1f57A627...2E4082761
0 HYPE0.000329671.25782867
Withdraw142389382025-09-19 5:02:37129 days ago1758258157IN
0x1f57A627...2E4082761
0 HYPE0.001635856.67677241
Deposit142125352025-09-18 21:49:47129 days ago1758232187IN
0x1f57A627...2E4082761
0 HYPE0.000043370.1306
Withdraw142088102025-09-18 20:48:43129 days ago1758228523IN
0x1f57A627...2E4082761
0 HYPE0.000033620.12829329
Withdraw141575122025-09-18 6:47:46130 days ago1758178066IN
0x1f57A627...2E4082761
0 HYPE0.000204430.78
Deposit141486532025-09-18 4:22:32130 days ago1758169352IN
0x1f57A627...2E4082761
0 HYPE0.000040450.12079225
Deposit141485672025-09-18 4:21:07130 days ago1758169267IN
0x1f57A627...2E4082761
0 HYPE0.000036430.10878344
Deposit141485182025-09-18 4:20:19130 days ago1758169219IN
0x1f57A627...2E4082761
0 HYPE0.000039020.11650307
Deposit141484892025-09-18 4:19:51130 days ago1758169191IN
0x1f57A627...2E4082761
0 HYPE0.000034190.10208962
Deposit141484612025-09-18 4:19:23130 days ago1758169163IN
0x1f57A627...2E4082761
0 HYPE0.000037840.11297455
Deposit141484342025-09-18 4:18:57130 days ago1758169137IN
0x1f57A627...2E4082761
0 HYPE0.000034380.10266884
Deposit141484122025-09-18 4:18:35130 days ago1758169115IN
0x1f57A627...2E4082761
0 HYPE0.000033490.1
Deposit141408592025-09-18 2:14:46130 days ago1758161686IN
0x1f57A627...2E4082761
0 HYPE0.001397534.24
Deposit141402572025-09-18 2:04:54130 days ago1758161094IN
0x1f57A627...2E4082761
0 HYPE0.0085401425.91
Deposit141402182025-09-18 2:04:15130 days ago1758161055IN
0x1f57A627...2E4082761
0 HYPE0.002340217.1
Deposit141184422025-09-17 20:07:16130 days ago1758139636IN
0x1f57A627...2E4082761
0 HYPE0.001698315.94
Deposit140396212025-09-16 22:35:07131 days ago1758062107IN
0x1f57A627...2E4082761
0 HYPE0.000031950.10271521
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
55211862025-06-11 21:12:00228 days ago1749676320  Contract Creation0 HYPE
Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
GaugeV2

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// 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 {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.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 GaugeV2 is IGauge, SystemEpoch {
    using SafeERC20 for IERC20;

    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 rewards;
    mapping(address => bool) public isReward;

    /// @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;

    uint256 internal _unlocked = 1;

    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);

    error Reentrancy();

    constructor(
        address _stake,
        address _internalBribe,
        address _externalBribe,
        address _ve,
        address _voter,
        address[] memory _allowedRewardTokens
    ) {
        stake = _stake;
        internal_bribe = _internalBribe;
        external_bribe = _externalBribe;
        ve = _ve;
        voter = _voter;

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

    modifier lock() {
        if (_unlocked != 1) {
            revert Reentrancy();
        }

        _unlocked = 2;
        _;
        _unlocked = 1;
    }

    /**
     * @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) IERC20(tokens[i]).safeTransfer(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, "amount > 0");
        _updateRewardForAllTokens();

        IERC20(stake).safeTransferFrom(msg.sender, address(this), amount);

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

        if (tokenId > 0) {
            require(IVotingEscrow(ve).ownerOf(tokenId) == msg.sender, "not owner");
            if (tokenIds[msg.sender] == 0) {
                tokenIds[msg.sender] = tokenId;
                IVoter(voter).attachTokenToGauge(tokenId, msg.sender);
            }
            require(tokenIds[msg.sender] == tokenId, "invalid token id");
        } 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;
        IERC20(stake).safeTransfer(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, "invalid reward");
        require(amount > 0, "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]) {
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
            rewardRate[token] = amount / EPOCH;
        } else {
            uint256 _remaining = periodFinish[token] - block.timestamp;
            uint256 _left = _remaining * rewardRate[token];
            require(amount > _left, "amount > _left");
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
            rewardRate[token] = (amount + _left) / EPOCH;
        }
        require(rewardRate[token] > 0, "reward rate is 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

File 3 of 14 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 4 of 14 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// 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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 8 of 14 : 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 9 of 14 : 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":"_internalBribe","type":"address"},{"internalType":"address","name":"_externalBribe","type":"address"},{"internalType":"address","name":"_ve","type":"address"},{"internalType":"address","name":"_voter","type":"address"},{"internalType":"address[]","name":"_allowedRewardTokens","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"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":[{"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":[{"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":[],"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"},{"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":[],"name":"stake","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":"ve","outputs":[{"internalType":"address","name":"","type":"address"}],"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"}]

6101206040523461029c5761307e8038038061001a816102a0565b92833981019060c08183031261029c57610033816102c5565b61003f602083016102c5565b61004b604084016102c5565b90610058606085016102c5565b92610065608086016102c5565b60a086015190956001600160401b03821161029c57019580601f8801121561029c578651966001600160401b0388116101c3578760051b906020806100ab8185016102a0565b809b8152019282010192831161029c57602001905b82821061028457505050600160145560805260c05260e05260a052610100525f5b81518110156101d7576001600160a01b036100fc82846102d9565b511661010b575b6001016100e1565b6001600160a01b0361011d82846102d9565b51165f908152600d60205260409020805460ff191660011790556001600160a01b0361014982846102d9565b511690600c5491680100000000000000008310156101c3576001830180600c558310156101af57600c5f527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c790920180546001600160a01b031916909217909155610103565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b604051612d9090816102ee82396080518181816102630152818161063201528181610a2801528181610b1e015281816115bf0152611ffc015260a0518181816106a8015281816112210152611a26015260c0518161135f015260e05181611afb0152610100518181816102ba015281816103800152818161076e0152818161087101528181610f7d015281816115270152818161169e015281816118e60152818161205201526121160152f35b60208091610291846102c5565b8152019101906100c0565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176101c357604052565b51906001600160a01b038216820361029c57565b80518210156101af5760209160051b01019056fe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301316ddf14611b2a5750806303fbf83a14611ae65780630cdfebfa14611a99578063115c6f3914611a7257806318160ddd14611a555780631f85071614611a11578063211dc32d146119e5578063221ca18c146119ad5780632ce9aead146119755780632e1a7d4d1461195757806331279d3d146115ee5780633a4b66f1146115a95780633ca068b61461155657806346c96aac146115115780634d5ce038146114d25780635a45d052146114ac578063638634ee1461146d57806363fb415b1461143457806368fcee1a146113e65780636fcfff45146113ad57806370a08231146109c457806376f4be361461138e578063770f857114611349578063853828b6146113235780639418f939146111d657806399bcc052146111b25780639ce43f9014611179578063a0dc27581461115b578063a495e5b514611108578063aa479652146110cf578063b66503cf14610ae3578063c6f678bd146109fd578063d35e2544146109c4578063d7da4bb0146109a7578063da09d19d1461096e578063e2bbb158146105f7578063e5748213146105d4578063e6886396146105b6578063e8111a1214610598578063f12297771461056c578063f301af4214610528578063f7412baf146104f4578063fc97a303146104bb578063fd314098146104875763fdb483c71461020d575f80fd5b346104845761021b36611bdf565b9060016014540361047557600260145581610234612c2c565b61024082600a54611c21565b600a55338452600b6020526040842061025a838254611c21565b905561029082337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612883565b156104615733835260096020526040832054820361043957338352600960205260408320839055827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104485760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561043d5761044c575b50505b338352600160205261033660408420548454611c21565b8355338352600b60205261035d604084205433855260016020528060408620558454611d9d565b835533835260016020526103756040842054336128c5565b61037d612995565b827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b1561044857604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d57610424575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a2600160145580f35b8161042e91611bbd565b61043957825f6103e9565b8280fd5b6040513d84823e3d90fd5b5080fd5b8161045691611bbd565b61043957825f61031c565b33835260096020526040832054915061031f565b63558a1e0360e11b8352600483fd5b80fd5b50346104845760403660031901126104845760406104af6104a6611b75565b60243590612462565b82519182526020820152f35b5034610484576020366003190112610484576020906040906001600160a01b036104e3611b75565b168152600983522054604051908152f35b5034610484576020366003190112610484576040809160043581526010602052206001815491015482519182526020820152f35b50346104845760203660031901126104845760043590600c5482101561048457602061055383611bf5565b905460405160039290921b1c6001600160a01b03168152f35b503461048457602036600319011261048457602061059061058b611b75565b6123b3565b604051908152f35b50346104845780600319360112610484576020601154604051908152f35b50346104845780600319360112610484576020600c54604051908152f35b5034610484578060031936011261048457602060ff600254166040519015158152f35b50346104845761060636611bdf565b906001601454036104755760026014558161062282151561237a565b61062a612c2c565b61065f8230337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612be8565b61066b82600a54611d9d565b600a55338452600b60205260408420610685838254611d9d565b90551561095a576040516331a9108f60e11b8152600481018390526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561094f578491610920575b50336001600160a01b03909116036108ef57338352600960205260408320541561085f575b338352600960205281604084205403610827575b338352600160205261073060408420548454611c21565b8355338352600b6020526107636040842054338552600160205280604086205561075b818654611d9d565b8555336128c5565b61076b612995565b827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104485760405163530e389d60e11b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d57610812575b505060405191825260208201527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1560403392a2600160145580f35b8161081c91611bbd565b61043957825f6107d7565b60405162461bcd60e51b815260206004820152601060248201526f1a5b9d985b1a59081d1bdad95b881a5960821b6044820152606490fd5b338352600960205260408320829055827f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156104485760405163698473e360e01b8152600481018590523360248201529082908290604490829084905af1801561043d576108da575b5050610705565b816108e491611bbd565b61043957825f6108d3565b60405162461bcd60e51b81526020600482015260096024820152683737ba1037bbb732b960b91b6044820152606490fd5b610942915060203d602011610948575b61093a8183611bbd565b81019061230b565b5f6106e0565b503d610930565b6040513d86823e3d90fd5b338352600960205260408320549150610719565b5034610484576020366003190112610484576020906040906001600160a01b03610996611b75565b168152600483522054604051908152f35b503461048457806003193601126104845760209054604051908152f35b5034610484576020366003190112610484576020906040906001600160a01b036109ec611b75565b168152600b83522054604051908152f35b5034610484576020366003190112610484576040516370a0823160e01b8152336004828101919091527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316919035602082602481865afa91821561094f578492610aab575b50600160145403610a9c57600260145561065f828294610a8b82151561237a565b610a93612c2c565b30903390612be8565b63558a1e0360e11b8452600484fd5b9091506020813d602011610adb575b81610ac760209383611bbd565b81010312610ad75751905f610a6a565b5f80fd5b3d9150610aba565b503461048457604036600319011261048457610afd611b75565b6024356001601454036104755760026014556001600160a01b0382811692907f000000000000000000000000000000000000000000000000000000000000000016831461109957610b4f82151561237a565b828452600d60205260ff60408520541615610f61575b8284526003602052604084205415610e6e575b610b81816125c2565b848652600660205260408620908587526005602052604087205555828452600460205260408420544210155f14610dd757610bbe82303386612be8565b828452600360205262093a80820460408520555b8284526003602052604084205415610d9f576040516370a0823160e01b8152306004820152602081602481875afa908115610d94578591610d62575b50838552600360205262093a806040862054910410610d1d5762093a804201804211610d095783855260046020526040852055828452600d60205260ff60408520541615610c8c575b506040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a3600160145580f35b828452600d60205260408420805460ff19166001179055600c5468010000000000000000811015610cf55790610ccb826001610cef9401600c55611bf5565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f610c57565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606490fd5b90506020813d602011610d8c575b81610d7d60209383611bbd565b81010312610ad757515f610c0e565b3d9150610d70565b6040513d87823e3d90fd5b60405162461bcd60e51b815260206004820152601060248201526f0726577617264207261746520697320360841b6044820152606490fd5b8284526004602052610e03610df0426040872054611c21565b8486526003602052604086205490611daa565b80831115610e3857610e2562093a8091610e1f85303389612be8565b84611d9d565b0483855260036020526040852055610bd2565b60405162461bcd60e51b815260206004820152600e60248201526d185b5bdd5b9d080f8817db19599d60921b6044820152606490fd5b8284526013602052604084205480151580610f24575b15610ec857838552601260205260408520905f198101908111610eb4578552602052836001604082200155610b78565b634e487b7160e01b86526011600452602486fd5b610f07604051610ed781611ba1565b42815286602082015285875260126020526040872083885260205260408720906020600191805184550151910155565b60018101809111610d095783855260136020526040852055610b78565b508385526012602052604085205f198201828111610f4d57865260205242604086205414610e84565b634e487b7160e01b87526011600452602487fd5b604051633af32abf60e01b8152600481018490526020816024817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610d9457859161105a575b501561100a576010600c5410610b655760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608490fd5b90506020813d602011611091575b8161107560209383611bbd565b8101031261108d5751801515810361108d575f610fb5565b8480fd5b3d9150611068565b60405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081c995dd85c9960921b6044820152606490fd5b5034610484576020366003190112610484576020906040906001600160a01b036110f7611b75565b168152601383522054604051908152f35b5034610484576040366003190112610484576040611124611b75565b9161112d611b8b565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b5034610484578060031936011261048457602060405162093a808152f35b5034610484576020366003190112610484576020906040906001600160a01b036111a1611b75565b168152600683522054604051908152f35b50346104845760203660031901126104845760206105906111d1611b75565b61232a565b5034610484576060366003190112610484576004356111f3611b8b565b6044356001600160a01b0381169290919083830361108d576040516342f9577960e11b8152602081600481897f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081156113185786916112f9575b506001600160a01b031633036112c85761127281611bf5565b90546001600160a01b039384169360039290921b1c1682900361108d576112c593610ccb928652600d6020526040862060ff1981541690558552600d60205260408520600160ff19825416179055611bf5565b80f35b60405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b6044820152606490fd5b611312915060203d6020116109485761093a8183611bbd565b5f611259565b6040513d88823e3d90fd5b5034610484578060031936011261048457338152600b6020526112c56040822054611f9d565b50346104845780600319360112610484576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461048457602036600319011261048457602061059060043561221f565b5034610484576020366003190112610484576020906040906001600160a01b036113d5611b75565b168152600f83522054604051908152f35b503461048457604036600319011261048457611400611b75565b61140c60243582612759565b909160018060a01b031690818452600660205260408420918452600560205260408420555580f35b5034610484576020366003190112610484576020906040906001600160a01b0361145c611b75565b168152600183522054604051908152f35b503461048457602036600319011261048457602061059061148c611b75565b60018060a01b03165f52600460205260405f205442811090421802421890565b5034610484576040366003190112610484576114c6611b75565b61140c60243582612a2b565b50346104845760203660031901126104845760209060ff906040906001600160a01b036114fd611b75565b168152600d84522054166040519015158152f35b50346104845780600319360112610484576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610484576040366003190112610484576040611572611b75565b9161157b611b8b565b9260018060a01b031681526008602052209060018060a01b03165f52602052602060405f2054604051908152f35b50346104845780600319360112610484576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610ad7576040366003190112610ad757611608611b75565b60243567ffffffffffffffff8111610ad75736602382011215610ad75780600401359067ffffffffffffffff8211611943578160051b90604051926116506020840185611bbd565b83526024602084019282010190368211610ad757602401915b81831061192357505050600160145403611914576001600160a01b0382169033821480156118e2575b15610ad75760016014557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610ad7575f80916024604051809481936363453ae160e01b83523060048401525af180156118d7576118c2575b506002601455835b815181101561185a576001906117266001600160a01b0361171f838661220b565b51166125c2565b838060a01b03611736848761220b565b5116885260066020526040882090848060a01b03611754858861220b565b51168952600560205260408920555561177e85838060a01b03611777848761220b565b5116611dbd565b828060a01b0361178e838661220b565b51168752600760205260408720855f5260205260405f20429055828060a01b036117b8838661220b565b5116875260066020526040872054838060a01b036117d6848761220b565b51168852600860205260408820865f5260205260405f205580611839575b828060a01b03611804838661220b565b5116906040519081527f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc960203392a3016116fe565b6118558187858060a01b0361184e868961220b565b5116612883565b6117f4565b846118b28585808452600160205261187760408520548554611c21565b8455808452600b60205261189e604085205482865260016020528060408720558554611d9d565b8455835260016020526040832054906128c5565b6118ba612995565b600160145580f35b6118cf9194505f90611bbd565b5f925f6116f6565b6040513d5f823e3d90fd5b50337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614611692565b63558a1e0360e11b5f5260045ffd5b82356001600160a01b0381168103610ad757815260209283019201611669565b634e487b7160e01b5f52604160045260245ffd5b34610ad7576020366003190112610ad757611973600435611f9d565b005b34610ad7576020366003190112610ad7576001600160a01b03611996611b75565b165f526005602052602060405f2054604051908152f35b34610ad7576020366003190112610ad7576001600160a01b036119ce611b75565b165f526003602052602060405f2054604051908152f35b34610ad7576040366003190112610ad7576020610590611a03611b75565b611a0b611b8b565b90611dbd565b34610ad7575f366003190112610ad7576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610ad7575f366003190112610ad7576020600a54604051908152f35b34610ad7576040366003190112610ad7576020610590611a90611b75565b60243590611c7e565b34610ad7576040366003190112610ad7576001600160a01b03611aba611b75565b165f52600e60205260405f206024355f526020526040805f206001815491015482519182526020820152f35b34610ad7575f366003190112610ad7576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b34610ad7576040366003190112610ad7576040906001600160a01b03611b4e611b75565b165f526012602052815f206024355f52602052815f20600181549101549082526020820152f35b600435906001600160a01b0382168203610ad757565b602435906001600160a01b0382168203610ad757565b6040810190811067ffffffffffffffff82111761194357604052565b90601f8019910116810190811067ffffffffffffffff82111761194357604052565b6040906003190112610ad7576004359060243590565b600c54811015611c0d57600c5f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b91908203918211611c2e57565b634e487b7160e01b5f52601160045260245ffd5b8115611c4c570490565b634e487b7160e01b5f52601260045260245ffd5b90604051611c6d81611ba1565b602060018294805484520154910152565b6001600160a01b03165f818152600f6020526040902054918215611d8857815f52600e60205260405f20925f1981019081119384611c2e57815f526020528160405f20541115611d8157825f52600e60205260405f205f80526020528160405f205411611d79575f93611c2e5791905b838311611cfb5750505090565b611d11611d088585611c21565b60011c84611c21565b93825f52600e60205260405f20855f52602052611d3060405f20611c60565b51828103611d4057505050505090565b918093949592105f14611d575750925b9190611cee565b93505f19810190811115611d5057634e487b7160e01b5f52601160045260245ffd5b505050505f90565b9250505090565b5050505f90565b5f198114611c2e5760010190565b91908201809211611c2e57565b81810292918115918404141715611c2e57565b6001600160a01b038181165f8181526007602090815260408083209487168084529482528083205484845260128352818420848052835281842054868552600f909352922054939592939092919015611f93578281611e229411908218021890611c7e565b92805f52600f60205260405f20545f19810190808211611c2e575f9582611ecb575b505092611ebb670de0b6b3a76400009383611ec89796611ec1955f52600e60205260405f20905f52602052611e7b60405f20611c60565b90611e966020611e8c845188612462565b50930151956123b3565b925f52600860205260405f20905f5260205260405f2054818111908218021890611c21565b90611daa565b0490611d9d565b90565b9692969391906001190181811196875b611c2e57818611611f7857885f52600e60205260405f20865f52602052611f0460405f20611c60565b895f52600e60205260405f20916001880192838911611c2e57611ec1611f6b93670de0b6b3a764000092611f71965f52602052611ebb8c6020611f61611f4c60405f20611c60565b92611f58865182612462565b50935190612462565b5093015192611c21565b95611d8f565b9487611edb565b93979396509193509050611ebb670de0b6b3a7640000611e44565b5050505050505f90565b5f905f91335f52600b60205260405f205482146121f6575b60016014540361191457600260145582611fcd612c2c565b611fd983600a54611c21565b600a55335f52600b60205260405f20611ff3848254611c21565b905561202983337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612883565b156121e157335f52600960205260405f20548303610ad757335f908152600960205260408120557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610ad75760405163411b1f7760e01b815260048101859052336024820152905f908290604490829084905af180156118d7576121ce575b505b33815260016020526120cd60408220548254611c21565b8155338152600b6020526120f4604082205433835260016020528060408420558254611d9d565b8155338152600160205261210c6040822054336128c5565b612114612995565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b1561044857604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d576121b9575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601455565b6121c4828092611bbd565b610484578061217f565b6121da91505f90611bbd565b5f5f6120b4565b9150335f52600960205260405f2054916120b6565b9150335f52600960205260405f205491611fb5565b8051821015611c0d5760209160051b010190565b601154908115612305575f1982019182119182611c2e57805f5260106020528160405f205411156122ff575f805260106020527f6e0956cda88cad152e89927e53611735b61a5c762d1428573c6931b0a5efcb01548210611d88575f92611c2e57905b82821161228e57505090565b6122a461229b8484611c21565b60011c83611c21565b92835f5260106020526122b960405f20611c60565b518281036122c8575050505090565b9180939492105f146122dd5750915b90612282565b92505f198101908111156122d757634e487b7160e01b5f52601160045260245ffd5b91505090565b50505f90565b90816020910312610ad757516001600160a01b0381168103610ad75790565b6001600160a01b03165f818152600460205260409020544210156123755780611ec8915f5260046020526123624260405f2054611c21565b905f52600360205260405f205490611daa565b505f90565b1561238157565b60405162461bcd60e51b815260206004820152600a6024820152690616d6f756e74203e20360b41b6044820152606490fd5b5f54908115612446576001600160a01b03165f8181526006602090815260408083205460048084528285205460058552929094205493909252909261241292909161236291808218818310021890428181119181189190910218611c21565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715611c2e57611ec89261244091611c42565b90611d9d565b6001600160a01b03165f90815260066020526040902054919050565b6001600160a01b03165f8181526013602052604090205490929181156125b857835f52601260205260405f20915f1981019081119283611c2e57815f526020528160405f2054111561259057845f52601260205260405f205f80526020528160405f205411612585575f92611c2e57905b8282116125005750505f928352601260209081526040808520928552919052909120600181015490549091565b61251061229b8484979597611c21565b835f52601260205260405f20815f5260205261252e60405f20611c60565b9586518381145f1461254a575050505050506020820151915190565b839495975092909192105f146125635750915b906124d3565b92505f1981019081111561255d57634e487b7160e01b5f52601160045260245ffd5b50505090505f905f90565b5f94855260126020908152604080872092875291905290932060018101549054909350919050565b505090505f905f90565b9060018060a01b038216805f52600560205260405f205492815f52600660205260405f205491601154908115612640575f52600360205260405f20541561274f5761260c8561221f565b5f1982019190818311611c2e57826126a7575b50505f52601060205261263460405f20611c60565b60208101908151612647575b5050509190565b829561269d949261268d926126796126949660018060a01b03165f52600460205260405f205442811090421802421890565b905191519180841184821802189089612c82565b5090611d9d565b92834291612b17565b42915f8080612640565b9491906001190181811195865b611c2e5781811161274357805f5260106020526126d360405f20611c60565b6020810180516126ef575b50506126e990611d8f565b866126b4565b90919860018a0190818b11611c2e576126e99361272f93612729935f52601060205261271d60405f20611c60565b5191519051918a612c82565b97611d9d565b9561273b818888612b17565b97905f6126de565b50509093505f8061261f565b5050909150904290565b919060018060a01b038316805f52600560205260405f205493815f52600660205260405f20549260115492831561287b575f52600360205260405f205415612870576127a48661221f565b905f198401938411611c2e578381109084180280841893036127c7575050509190565b5f19830192831194919290855b611c2e5781811161286357805f5260106020526127f360405f20611c60565b60208101805161280f575b505061280990611d8f565b856127d4565b9091976001890190818a11611c2e576128099361284f93612849935f52601060205261283d60405f20611c60565b51915190519189612c82565b96611d9d565b9461285b818787612b17565b96905f6127fe565b50509250505f8080612640565b505050909150904290565b505050509190565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526128c3916128be606483611bbd565b612d02565b565b6001600160a01b03165f818152600f6020526040902054908115158061296c575b15612911575f52600e60205260405f20905f198101908111611c2e575f52602052600160405f200155565b9091612953906040519061292482611ba1565b4282526020820152825f52600e60205260405f20845f5260205260405f20906020600191805184550151910155565b60018201809211611c2e575f52600f60205260405f2055565b50805f52600e60205260405f205f198301838111611c2e575f526020524260405f2054146128e6565b60115480151580612a0c575b156129c5575f54905f198101908111611c2e575f526010602052600160405f200155565b6129fc5f54604051906129d782611ba1565b4282526020820152825f52601060205260405f20906020600191805184550151910155565b60018101809111611c2e57601155565b505f198101818111611c2e575f5260106020524260405f2054146129a1565b919060018060a01b03831690815f52600560205260405f205493825f52600660205260405f20549260115490811561287b575f52600360205260405f20541561287057612a778661221f565b925f198201918211611c2e578181109082180218915b828110612a9b575050509190565b805f526010602052612aaf60405f20611c60565b602081018051612ac4575b5050600101612a8d565b9091966001880190818911611c2e57600193612b0393612afd935f526010602052612af160405f20611c60565b51915190519187612c82565b95611d9d565b93612b0f818685612b17565b95905f612aba565b6001600160a01b03165f81815260136020526040902054919282151580612bbf575b15612b6557505f52601260205260405f20905f198101908111611c2e575f52602052600160405f200155565b612ba691929360405191612b7883611ba1565b82526020820152825f52601260205260405f20845f5260205260405f20906020600191805184550151910155565b60018201809211611c2e575f52601360205260405f2055565b50815f52601260205260405f205f198401848111611c2e575f526020528060405f205414612b39565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526128c3916128be608483611bbd565b600c545f5b818110612c3c575050565b80612c48600192611bf5565b838060a01b0391549060031b1c16612c5f816125c2565b825f93929352600660205260405f20915f52600560205260405f20555501612c31565b6001600160a01b03165f81815260046020526040902054828618838711029092189594612cd39391926123629280831892811092909202909118808218818310021890808818908811028718611c21565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715611c2e57612cfe91611c42565b9190565b905f602091828151910182855af1156118d7575f513d612d5157506001600160a01b0381163b155b612d315750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415612d2a56fea264697066735822122094a5245624aec3c2a4f08b600b7911784f77ecb7641ec165be4b80bec26c266264736f6c634300081c003300000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff000000000000000000000000ccd2f9920f45308652667bcadce4e91080edafb5000000000000000000000000d86cde14fe4310feb50558c92b0eaec56bb7aed9000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8000000000000000000000000b50a96253abdf803d85efcdce07ad8becbc52bd500000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301316ddf14611b2a5750806303fbf83a14611ae65780630cdfebfa14611a99578063115c6f3914611a7257806318160ddd14611a555780631f85071614611a11578063211dc32d146119e5578063221ca18c146119ad5780632ce9aead146119755780632e1a7d4d1461195757806331279d3d146115ee5780633a4b66f1146115a95780633ca068b61461155657806346c96aac146115115780634d5ce038146114d25780635a45d052146114ac578063638634ee1461146d57806363fb415b1461143457806368fcee1a146113e65780636fcfff45146113ad57806370a08231146109c457806376f4be361461138e578063770f857114611349578063853828b6146113235780639418f939146111d657806399bcc052146111b25780639ce43f9014611179578063a0dc27581461115b578063a495e5b514611108578063aa479652146110cf578063b66503cf14610ae3578063c6f678bd146109fd578063d35e2544146109c4578063d7da4bb0146109a7578063da09d19d1461096e578063e2bbb158146105f7578063e5748213146105d4578063e6886396146105b6578063e8111a1214610598578063f12297771461056c578063f301af4214610528578063f7412baf146104f4578063fc97a303146104bb578063fd314098146104875763fdb483c71461020d575f80fd5b346104845761021b36611bdf565b9060016014540361047557600260145581610234612c2c565b61024082600a54611c21565b600a55338452600b6020526040842061025a838254611c21565b905561029082337f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff6001600160a01b0316612883565b156104615733835260096020526040832054820361043957338352600960205260408320839055827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104485760405163411b1f7760e01b8152600481018590523360248201529082908290604490829084905af1801561043d5761044c575b50505b338352600160205261033660408420548454611c21565b8355338352600b60205261035d604084205433855260016020528060408620558454611d9d565b835533835260016020526103756040842054336128c5565b61037d612995565b827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b1561044857604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d57610424575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a2600160145580f35b8161042e91611bbd565b61043957825f6103e9565b8280fd5b6040513d84823e3d90fd5b5080fd5b8161045691611bbd565b61043957825f61031c565b33835260096020526040832054915061031f565b63558a1e0360e11b8352600483fd5b80fd5b50346104845760403660031901126104845760406104af6104a6611b75565b60243590612462565b82519182526020820152f35b5034610484576020366003190112610484576020906040906001600160a01b036104e3611b75565b168152600983522054604051908152f35b5034610484576020366003190112610484576040809160043581526010602052206001815491015482519182526020820152f35b50346104845760203660031901126104845760043590600c5482101561048457602061055383611bf5565b905460405160039290921b1c6001600160a01b03168152f35b503461048457602036600319011261048457602061059061058b611b75565b6123b3565b604051908152f35b50346104845780600319360112610484576020601154604051908152f35b50346104845780600319360112610484576020600c54604051908152f35b5034610484578060031936011261048457602060ff600254166040519015158152f35b50346104845761060636611bdf565b906001601454036104755760026014558161062282151561237a565b61062a612c2c565b61065f8230337f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff6001600160a01b0316612be8565b61066b82600a54611d9d565b600a55338452600b60205260408420610685838254611d9d565b90551561095a576040516331a9108f60e11b8152600481018390526020816024817f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165afa90811561094f578491610920575b50336001600160a01b03909116036108ef57338352600960205260408320541561085f575b338352600960205281604084205403610827575b338352600160205261073060408420548454611c21565b8355338352600b6020526107636040842054338552600160205280604086205561075b818654611d9d565b8555336128c5565b61076b612995565b827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104485760405163530e389d60e11b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d57610812575b505060405191825260208201527f90890809c654f11d6e72a28fa60149770a0d11ec6c92319d6ceb2bb0a4ea1a1560403392a2600160145580f35b8161081c91611bbd565b61043957825f6107d7565b60405162461bcd60e51b815260206004820152601060248201526f1a5b9d985b1a59081d1bdad95b881a5960821b6044820152606490fd5b338352600960205260408320829055827f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b156104485760405163698473e360e01b8152600481018590523360248201529082908290604490829084905af1801561043d576108da575b5050610705565b816108e491611bbd565b61043957825f6108d3565b60405162461bcd60e51b81526020600482015260096024820152683737ba1037bbb732b960b91b6044820152606490fd5b610942915060203d602011610948575b61093a8183611bbd565b81019061230b565b5f6106e0565b503d610930565b6040513d86823e3d90fd5b338352600960205260408320549150610719565b5034610484576020366003190112610484576020906040906001600160a01b03610996611b75565b168152600483522054604051908152f35b503461048457806003193601126104845760209054604051908152f35b5034610484576020366003190112610484576020906040906001600160a01b036109ec611b75565b168152600b83522054604051908152f35b5034610484576020366003190112610484576040516370a0823160e01b8152336004828101919091527f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff6001600160a01b0316919035602082602481865afa91821561094f578492610aab575b50600160145403610a9c57600260145561065f828294610a8b82151561237a565b610a93612c2c565b30903390612be8565b63558a1e0360e11b8452600484fd5b9091506020813d602011610adb575b81610ac760209383611bbd565b81010312610ad75751905f610a6a565b5f80fd5b3d9150610aba565b503461048457604036600319011261048457610afd611b75565b6024356001601454036104755760026014556001600160a01b0382811692907f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff16831461109957610b4f82151561237a565b828452600d60205260ff60408520541615610f61575b8284526003602052604084205415610e6e575b610b81816125c2565b848652600660205260408620908587526005602052604087205555828452600460205260408420544210155f14610dd757610bbe82303386612be8565b828452600360205262093a80820460408520555b8284526003602052604084205415610d9f576040516370a0823160e01b8152306004820152602081602481875afa908115610d94578591610d62575b50838552600360205262093a806040862054910410610d1d5762093a804201804211610d095783855260046020526040852055828452600d60205260ff60408520541615610c8c575b506040519081527ff70d5c697de7ea828df48e5c4573cb2194c659f1901f70110c52b066dcf5082660203392a3600160145580f35b828452600d60205260408420805460ff19166001179055600c5468010000000000000000811015610cf55790610ccb826001610cef9401600c55611bf5565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f610c57565b634e487b7160e01b85526041600452602485fd5b634e487b7160e01b85526011600452602485fd5b60405162461bcd60e51b815260206004820152601860248201527f50726f76696465642072657761726420746f6f206869676800000000000000006044820152606490fd5b90506020813d602011610d8c575b81610d7d60209383611bbd565b81010312610ad757515f610c0e565b3d9150610d70565b6040513d87823e3d90fd5b60405162461bcd60e51b815260206004820152601060248201526f0726577617264207261746520697320360841b6044820152606490fd5b8284526004602052610e03610df0426040872054611c21565b8486526003602052604086205490611daa565b80831115610e3857610e2562093a8091610e1f85303389612be8565b84611d9d565b0483855260036020526040852055610bd2565b60405162461bcd60e51b815260206004820152600e60248201526d185b5bdd5b9d080f8817db19599d60921b6044820152606490fd5b8284526013602052604084205480151580610f24575b15610ec857838552601260205260408520905f198101908111610eb4578552602052836001604082200155610b78565b634e487b7160e01b86526011600452602486fd5b610f07604051610ed781611ba1565b42815286602082015285875260126020526040872083885260205260408720906020600191805184550151910155565b60018101809111610d095783855260136020526040852055610b78565b508385526012602052604085205f198201828111610f4d57865260205242604086205414610e84565b634e487b7160e01b87526011600452602487fd5b604051633af32abf60e01b8152600481018490526020816024817f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b03165afa908115610d9457859161105a575b501561100a576010600c5410610b655760405162461bcd60e51b815260206004820152601760248201527f746f6f206d616e79207265776172647320746f6b656e730000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152602260248201527f7265776172647320746f6b656e73206d7573742062652077686974656c697374604482015261195960f21b6064820152608490fd5b90506020813d602011611091575b8161107560209383611bbd565b8101031261108d5751801515810361108d575f610fb5565b8480fd5b3d9150611068565b60405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a59081c995dd85c9960921b6044820152606490fd5b5034610484576020366003190112610484576020906040906001600160a01b036110f7611b75565b168152601383522054604051908152f35b5034610484576040366003190112610484576040611124611b75565b9161112d611b8b565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b5034610484578060031936011261048457602060405162093a808152f35b5034610484576020366003190112610484576020906040906001600160a01b036111a1611b75565b168152600683522054604051908152f35b50346104845760203660031901126104845760206105906111d1611b75565b61232a565b5034610484576060366003190112610484576004356111f3611b8b565b6044356001600160a01b0381169290919083830361108d576040516342f9577960e11b8152602081600481897f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03165af19081156113185786916112f9575b506001600160a01b031633036112c85761127281611bf5565b90546001600160a01b039384169360039290921b1c1682900361108d576112c593610ccb928652600d6020526040862060ff1981541690558552600d60205260408520600160ff19825416179055611bf5565b80f35b60405162461bcd60e51b81526020600482015260096024820152686f6e6c79207465616d60b81b6044820152606490fd5b611312915060203d6020116109485761093a8183611bbd565b5f611259565b6040513d88823e3d90fd5b5034610484578060031936011261048457338152600b6020526112c56040822054611f9d565b50346104845780600319360112610484576040517f000000000000000000000000ccd2f9920f45308652667bcadce4e91080edafb56001600160a01b03168152602090f35b503461048457602036600319011261048457602061059060043561221f565b5034610484576020366003190112610484576020906040906001600160a01b036113d5611b75565b168152600f83522054604051908152f35b503461048457604036600319011261048457611400611b75565b61140c60243582612759565b909160018060a01b031690818452600660205260408420918452600560205260408420555580f35b5034610484576020366003190112610484576020906040906001600160a01b0361145c611b75565b168152600183522054604051908152f35b503461048457602036600319011261048457602061059061148c611b75565b60018060a01b03165f52600460205260405f205442811090421802421890565b5034610484576040366003190112610484576114c6611b75565b61140c60243582612a2b565b50346104845760203660031901126104845760209060ff906040906001600160a01b036114fd611b75565b168152600d84522054166040519015158152f35b50346104845780600319360112610484576040517f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b03168152602090f35b5034610484576040366003190112610484576040611572611b75565b9161157b611b8b565b9260018060a01b031681526008602052209060018060a01b03165f52602052602060405f2054604051908152f35b50346104845780600319360112610484576040517f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff6001600160a01b03168152602090f35b5034610ad7576040366003190112610ad757611608611b75565b60243567ffffffffffffffff8111610ad75736602382011215610ad75780600401359067ffffffffffffffff8211611943578160051b90604051926116506020840185611bbd565b83526024602084019282010190368211610ad757602401915b81831061192357505050600160145403611914576001600160a01b0382169033821480156118e2575b15610ad75760016014557f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b15610ad7575f80916024604051809481936363453ae160e01b83523060048401525af180156118d7576118c2575b506002601455835b815181101561185a576001906117266001600160a01b0361171f838661220b565b51166125c2565b838060a01b03611736848761220b565b5116885260066020526040882090848060a01b03611754858861220b565b51168952600560205260408920555561177e85838060a01b03611777848761220b565b5116611dbd565b828060a01b0361178e838661220b565b51168752600760205260408720855f5260205260405f20429055828060a01b036117b8838661220b565b5116875260066020526040872054838060a01b036117d6848761220b565b51168852600860205260408820865f5260205260405f205580611839575b828060a01b03611804838661220b565b5116906040519081527f9aa05b3d70a9e3e2f004f039648839560576334fb45c81f91b6db03ad9e2efc960203392a3016116fe565b6118558187858060a01b0361184e868961220b565b5116612883565b6117f4565b846118b28585808452600160205261187760408520548554611c21565b8455808452600b60205261189e604085205482865260016020528060408720558554611d9d565b8455835260016020526040832054906128c5565b6118ba612995565b600160145580f35b6118cf9194505f90611bbd565b5f925f6116f6565b6040513d5f823e3d90fd5b50337f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b031614611692565b63558a1e0360e11b5f5260045ffd5b82356001600160a01b0381168103610ad757815260209283019201611669565b634e487b7160e01b5f52604160045260245ffd5b34610ad7576020366003190112610ad757611973600435611f9d565b005b34610ad7576020366003190112610ad7576001600160a01b03611996611b75565b165f526005602052602060405f2054604051908152f35b34610ad7576020366003190112610ad7576001600160a01b036119ce611b75565b165f526003602052602060405f2054604051908152f35b34610ad7576040366003190112610ad7576020610590611a03611b75565b611a0b611b8b565b90611dbd565b34610ad7575f366003190112610ad7576040517f000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e236001600160a01b03168152602090f35b34610ad7575f366003190112610ad7576020600a54604051908152f35b34610ad7576040366003190112610ad7576020610590611a90611b75565b60243590611c7e565b34610ad7576040366003190112610ad7576001600160a01b03611aba611b75565b165f52600e60205260405f206024355f526020526040805f206001815491015482519182526020820152f35b34610ad7575f366003190112610ad7576040517f000000000000000000000000d86cde14fe4310feb50558c92b0eaec56bb7aed96001600160a01b03168152602090f35b34610ad7576040366003190112610ad7576040906001600160a01b03611b4e611b75565b165f526012602052815f206024355f52602052815f20600181549101549082526020820152f35b600435906001600160a01b0382168203610ad757565b602435906001600160a01b0382168203610ad757565b6040810190811067ffffffffffffffff82111761194357604052565b90601f8019910116810190811067ffffffffffffffff82111761194357604052565b6040906003190112610ad7576004359060243590565b600c54811015611c0d57600c5f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b91908203918211611c2e57565b634e487b7160e01b5f52601160045260245ffd5b8115611c4c570490565b634e487b7160e01b5f52601260045260245ffd5b90604051611c6d81611ba1565b602060018294805484520154910152565b6001600160a01b03165f818152600f6020526040902054918215611d8857815f52600e60205260405f20925f1981019081119384611c2e57815f526020528160405f20541115611d8157825f52600e60205260405f205f80526020528160405f205411611d79575f93611c2e5791905b838311611cfb5750505090565b611d11611d088585611c21565b60011c84611c21565b93825f52600e60205260405f20855f52602052611d3060405f20611c60565b51828103611d4057505050505090565b918093949592105f14611d575750925b9190611cee565b93505f19810190811115611d5057634e487b7160e01b5f52601160045260245ffd5b505050505f90565b9250505090565b5050505f90565b5f198114611c2e5760010190565b91908201809211611c2e57565b81810292918115918404141715611c2e57565b6001600160a01b038181165f8181526007602090815260408083209487168084529482528083205484845260128352818420848052835281842054868552600f909352922054939592939092919015611f93578281611e229411908218021890611c7e565b92805f52600f60205260405f20545f19810190808211611c2e575f9582611ecb575b505092611ebb670de0b6b3a76400009383611ec89796611ec1955f52600e60205260405f20905f52602052611e7b60405f20611c60565b90611e966020611e8c845188612462565b50930151956123b3565b925f52600860205260405f20905f5260205260405f2054818111908218021890611c21565b90611daa565b0490611d9d565b90565b9692969391906001190181811196875b611c2e57818611611f7857885f52600e60205260405f20865f52602052611f0460405f20611c60565b895f52600e60205260405f20916001880192838911611c2e57611ec1611f6b93670de0b6b3a764000092611f71965f52602052611ebb8c6020611f61611f4c60405f20611c60565b92611f58865182612462565b50935190612462565b5093015192611c21565b95611d8f565b9487611edb565b93979396509193509050611ebb670de0b6b3a7640000611e44565b5050505050505f90565b5f905f91335f52600b60205260405f205482146121f6575b60016014540361191457600260145582611fcd612c2c565b611fd983600a54611c21565b600a55335f52600b60205260405f20611ff3848254611c21565b905561202983337f00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff6001600160a01b0316612883565b156121e157335f52600960205260405f20548303610ad757335f908152600960205260408120557f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b15610ad75760405163411b1f7760e01b815260048101859052336024820152905f908290604490829084905af180156118d7576121ce575b505b33815260016020526120cd60408220548254611c21565b8155338152600b6020526120f4604082205433835260016020528060408420558254611d9d565b8155338152600160205261210c6040822054336128c5565b612114612995565b7f000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a6001600160a01b0316803b1561044857604051633aa53b9160e21b815260048101859052336024820152604481018490529082908290606490829084905af1801561043d576121b9575b505060405191825260208201527ff279e6a1f5e320cca91135676d9cb6e44ca8a08c0b88342bcdb1144f6511b56860403392a26001601455565b6121c4828092611bbd565b610484578061217f565b6121da91505f90611bbd565b5f5f6120b4565b9150335f52600960205260405f2054916120b6565b9150335f52600960205260405f205491611fb5565b8051821015611c0d5760209160051b010190565b601154908115612305575f1982019182119182611c2e57805f5260106020528160405f205411156122ff575f805260106020527f6e0956cda88cad152e89927e53611735b61a5c762d1428573c6931b0a5efcb01548210611d88575f92611c2e57905b82821161228e57505090565b6122a461229b8484611c21565b60011c83611c21565b92835f5260106020526122b960405f20611c60565b518281036122c8575050505090565b9180939492105f146122dd5750915b90612282565b92505f198101908111156122d757634e487b7160e01b5f52601160045260245ffd5b91505090565b50505f90565b90816020910312610ad757516001600160a01b0381168103610ad75790565b6001600160a01b03165f818152600460205260409020544210156123755780611ec8915f5260046020526123624260405f2054611c21565b905f52600360205260405f205490611daa565b505f90565b1561238157565b60405162461bcd60e51b815260206004820152600a6024820152690616d6f756e74203e20360b41b6044820152606490fd5b5f54908115612446576001600160a01b03165f8181526006602090815260408083205460048084528285205460058552929094205493909252909261241292909161236291808218818310021890428181119181189190910218611c21565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715611c2e57611ec89261244091611c42565b90611d9d565b6001600160a01b03165f90815260066020526040902054919050565b6001600160a01b03165f8181526013602052604090205490929181156125b857835f52601260205260405f20915f1981019081119283611c2e57815f526020528160405f2054111561259057845f52601260205260405f205f80526020528160405f205411612585575f92611c2e57905b8282116125005750505f928352601260209081526040808520928552919052909120600181015490549091565b61251061229b8484979597611c21565b835f52601260205260405f20815f5260205261252e60405f20611c60565b9586518381145f1461254a575050505050506020820151915190565b839495975092909192105f146125635750915b906124d3565b92505f1981019081111561255d57634e487b7160e01b5f52601160045260245ffd5b50505090505f905f90565b5f94855260126020908152604080872092875291905290932060018101549054909350919050565b505090505f905f90565b9060018060a01b038216805f52600560205260405f205492815f52600660205260405f205491601154908115612640575f52600360205260405f20541561274f5761260c8561221f565b5f1982019190818311611c2e57826126a7575b50505f52601060205261263460405f20611c60565b60208101908151612647575b5050509190565b829561269d949261268d926126796126949660018060a01b03165f52600460205260405f205442811090421802421890565b905191519180841184821802189089612c82565b5090611d9d565b92834291612b17565b42915f8080612640565b9491906001190181811195865b611c2e5781811161274357805f5260106020526126d360405f20611c60565b6020810180516126ef575b50506126e990611d8f565b866126b4565b90919860018a0190818b11611c2e576126e99361272f93612729935f52601060205261271d60405f20611c60565b5191519051918a612c82565b97611d9d565b9561273b818888612b17565b97905f6126de565b50509093505f8061261f565b5050909150904290565b919060018060a01b038316805f52600560205260405f205493815f52600660205260405f20549260115492831561287b575f52600360205260405f205415612870576127a48661221f565b905f198401938411611c2e578381109084180280841893036127c7575050509190565b5f19830192831194919290855b611c2e5781811161286357805f5260106020526127f360405f20611c60565b60208101805161280f575b505061280990611d8f565b856127d4565b9091976001890190818a11611c2e576128099361284f93612849935f52601060205261283d60405f20611c60565b51915190519189612c82565b96611d9d565b9461285b818787612b17565b96905f6127fe565b50509250505f8080612640565b505050909150904290565b505050509190565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526128c3916128be606483611bbd565b612d02565b565b6001600160a01b03165f818152600f6020526040902054908115158061296c575b15612911575f52600e60205260405f20905f198101908111611c2e575f52602052600160405f200155565b9091612953906040519061292482611ba1565b4282526020820152825f52600e60205260405f20845f5260205260405f20906020600191805184550151910155565b60018201809211611c2e575f52600f60205260405f2055565b50805f52600e60205260405f205f198301838111611c2e575f526020524260405f2054146128e6565b60115480151580612a0c575b156129c5575f54905f198101908111611c2e575f526010602052600160405f200155565b6129fc5f54604051906129d782611ba1565b4282526020820152825f52601060205260405f20906020600191805184550151910155565b60018101809111611c2e57601155565b505f198101818111611c2e575f5260106020524260405f2054146129a1565b919060018060a01b03831690815f52600560205260405f205493825f52600660205260405f20549260115490811561287b575f52600360205260405f20541561287057612a778661221f565b925f198201918211611c2e578181109082180218915b828110612a9b575050509190565b805f526010602052612aaf60405f20611c60565b602081018051612ac4575b5050600101612a8d565b9091966001880190818911611c2e57600193612b0393612afd935f526010602052612af160405f20611c60565b51915190519187612c82565b95611d9d565b93612b0f818685612b17565b95905f612aba565b6001600160a01b03165f81815260136020526040902054919282151580612bbf575b15612b6557505f52601260205260405f20905f198101908111611c2e575f52602052600160405f200155565b612ba691929360405191612b7883611ba1565b82526020820152825f52601260205260405f20845f5260205260405f20906020600191805184550151910155565b60018201809211611c2e575f52601360205260405f2055565b50815f52601260205260405f205f198401848111611c2e575f526020528060405f205414612b39565b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526128c3916128be608483611bbd565b600c545f5b818110612c3c575050565b80612c48600192611bf5565b838060a01b0391549060031b1c16612c5f816125c2565b825f93929352600660205260405f20915f52600560205260405f20555501612c31565b6001600160a01b03165f81815260046020526040902054828618838711029092189594612cd39391926123629280831892811092909202909118808218818310021890808818908811028718611c21565b90670de0b6b3a7640000820291808304670de0b6b3a76400001490151715611c2e57612cfe91611c42565b9190565b905f602091828151910182855af1156118d7575f513d612d5157506001600160a01b0381163b155b612d315750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415612d2a56fea264697066735822122094a5245624aec3c2a4f08b600b7911784f77ecb7641ec165be4b80bec26c266264736f6c634300081c0033

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

00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff000000000000000000000000ccd2f9920f45308652667bcadce4e91080edafb5000000000000000000000000d86cde14fe4310feb50558c92b0eaec56bb7aed9000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a00000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8000000000000000000000000b50a96253abdf803d85efcdce07ad8becbc52bd500000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db

-----Decoded View---------------
Arg [0] : _stake (address): 0x94cEdCB72Fd719EC7F28F6c7DAeeCA24871DBbff
Arg [1] : _internalBribe (address): 0xccd2F9920f45308652667bCaDcE4e91080eDafB5
Arg [2] : _externalBribe (address): 0xD86cDE14fe4310fEb50558C92B0EaeC56bb7AeD9
Arg [3] : _ve (address): 0xdB9A1bdc443dd11366b8a6dc8038144eCc4D4E23
Arg [4] : _voter (address): 0xF3113E4F80c84935E576CFD75F4423E9B911908A
Arg [5] : _allowedRewardTokens (address[]): 0x8fF0dd9f9C40a0d76eF1BcFAF5f98c1610c74Bd8,0xb50A96253aBDF803D85efcDce07Ad8becBc52BD5,0x28245AB01298eaEf7933bC90d35Bd9DbCA5C89DB

-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 00000000000000000000000094cedcb72fd719ec7f28f6c7daeeca24871dbbff
Arg [1] : 000000000000000000000000ccd2f9920f45308652667bcadce4e91080edafb5
Arg [2] : 000000000000000000000000d86cde14fe4310feb50558c92b0eaec56bb7aed9
Arg [3] : 000000000000000000000000db9a1bdc443dd11366b8a6dc8038144ecc4d4e23
Arg [4] : 000000000000000000000000f3113e4f80c84935e576cfd75f4423e9b911908a
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [7] : 0000000000000000000000008ff0dd9f9c40a0d76ef1bcfaf5f98c1610c74bd8
Arg [8] : 000000000000000000000000b50a96253abdf803d85efcdce07ad8becbc52bd5
Arg [9] : 00000000000000000000000028245ab01298eaef7933bc90d35bd9dbca5c89db


Block Transaction Gas Used Reward
view all blocks ##produced##

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

Validator Index Block Amount
View All Withdrawals

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