ETH Price: $3,215.60 (+2.15%)
Gas: 6 Gwei

Token

Mev Liquid Staking Receipt (mevETH)
 

Overview

Max Total Supply

8,315.485470943078521281 mevETH

Holders

372 (0.00%)

Total Transfers

-

Market

Price

$3,265.94 @ 1.015655 ETH (+2.28%)

Onchain Market Cap

$27,157,876.62

Circulating Supply Market Cap

$27,157,896.00

Other Info

Token Contract (WITH 18 Decimals)

Loading...
Loading
Loading...
Loading
Loading...
Loading

OVERVIEW

mevETH is the non-custodial liquid staking receipt that offers the highest potential rewards today. MEV Auction enables participating LST's to earn new sources of revenue for their validator set.

# Exchange Pair Price  24H Volume % Volume

Contract Source Code Verified (Exact Match)

Contract Name:
MevEth

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 512 runs

Other Settings:
paris EvmVersion
File 1 of 11 : MevEth.sol
/// SPDX-License-Identifier: SSPL-1.-0

pragma solidity ^0.8.19;

/*///////////// Mev Protocol ///////////////////////
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⣦⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⣿⣿⣿⣷⣤⣀⠀⠀⠀⠀⠀⠉⠑⣶⣤⣄⣀⣠⣤⣶⣶⣿⣿⣿⣿⡇⠀⠀⠀
⠀⠀⠀⠀⣀⣴⣶⣿⣷⡄⠀⠀⠀⠀⢹⣿⣿⣿⣿⠏⠁⠀⢀⠄⠀⠀⠈⢀⠄⠀⢀⡖⠁⠀⢀⠀⠈⠻⣿⣿⣿⣿⡏⠀⠀⠀⠀
⠀⠀⢠⣾⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⢸⣿⣿⠏⠀⠀⢀⡴⠁⠀⠀⣠⠖⠁⢀⠞⠋⠀⢠⡇⢸⡄⠀⠀⠈⢻⣿⣿⠁⠀⠀⠀⠀
⠀⣠⣿⣿⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⢸⡿⠁⠀⠀⢀⡞⠀⠀⢀⡴⠃⠀⣰⠋⠀⠀⣰⡿⠀⡜⢳⡀⠘⣦⠀⢿⡇⠀⠀⠀⠀⠀
⢠⣿⣿⣿⣿⡿⠃⠀⠀⠀⠀⠀⠀⢰⣿⠃⠀⢀⠆⡞⡄⠀⣠⡞⠁⣀⢾⠃⠀⣀⡜⢱⠇⣰⠁⠈⣷⠂⢸⡇⠸⣵⠀⠀⠀⠀⠀
⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠀⠀⢠⣿⠇⠀⠀⡜⣸⡟⢀⣴⡏⢠⣾⠋⡎⢀⣼⠋⢀⡎⡰⠃⠀⠀⣿⣓⢒⡇⠀⣿⠀⠀⠀⠀⠀
⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠴⢻⣟⢀⣀⢀⣧⡇⢨⠟⢾⣔⡿⠃⢸⢀⠞⠃⢀⣾⡜⠁⠀⠀⠀⡏⠁⢠⠃⠀⢹⠀⠀⠀⠀⠀
⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⢸⣼⢸⣿⡟⢻⣿⠿⣶⣿⣿⣿⣶⣾⣏⣀⣠⣾⣿⠔⠒⠉⠉⢠⠁⡆⡸⠀⡈⣸⠀⠀⠀⠀⠀
⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⣸⣿⣸⣿⣇⢸⠃⡄⢻⠃⣾⣿⢋⠘⣿⣿⠏⣿⡟⣛⡛⢻⣿⢿⣶⣷⣿⣶⢃⣿⠀⠀⠀⠀⠀
⢸⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⣰⠃⣿⣿⣿⣿⠀⣸⣧⠈⣸⣿⠃⠘⠃⢹⣿⠀⣿⠃⠛⠛⣿⡇⢸⣿⡇⢸⣿⡿⣿⡀⠀⠀⠀⠀
⠀⠻⣿⣿⣿⣿⣦⡀⠀⢀⡔⣹⣼⡟⡟⣿⣿⣿⠛⠻⠶⠿⠷⣾⣿⣿⣬⣿⣠⣿⣀⣿⣿⣿⡇⠸⡿⠀⣾⡏⢠⣿⣇⠀⠀⠀⠀
⠀⠀⠙⢿⣿⣿⣿⣿⣷⡞⢠⣿⢿⡇⣿⡹⡝⢿⡷⣄⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠙⠛⠛⠻⠿⣶⣶⣾⣿⣇⣾⠉⢯⠃⠀⠀⠀
⠀⠀⠀⠀⠙⠿⣿⣿⣿⠇⢸⠇⠘⣇⠸⡇⣿⣮⣳⡀⠉⠂⠀⠀⣀⣤⡤⢤⣀⠀⠀⠀⠀⠀⢈⣿⠟⣠⣾⠿⣿⡆⡄⣧⡀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠙⠻⡘⠾⣄⠀⠘⢦⣿⠃⠹⣿⣿⣶⠤⠀⠀⣿⠋⠉⠻⣿⠁⠀⠠⣀⣤⣾⣵⣾⡿⠃⣾⠏⣿⣧⠋⡇⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣠⠖⠳⣄⡈⠃⠀⠼⠋⠙⢷⣞⢻⣿⣿⣀⡀⠈⠤⣀⠬⠟⠀⢀⣠⣶⠿⢛⡽⠋⣠⣾⣏⣠⡿⣃⣞⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣧⠀⠀⠀⠉⠛⠓⠢⠶⣶⡤⠺⡟⢺⣿⠿⣿⣶⣤⣀⣠⣴⣾⡿⠿⢵⠋⠙⠲⣏⡝⠁⠀⣹⢿⡣⣌⠒⠄⠀
⠀⠀⠀⠀⠀⠀⢸⠈⡄⠀⠇⠀⠀⡖⠁⢢⡞⠀⢰⠻⣆⡏⣇⠙⠻⣿⣿⣿⣿⠋⢀⡴⣪⢷⡀⠀⡘⠀⢀⠜⠁⢀⠟⢆⠑⢄⠀
⠀⠀⠀⠀⠀⠀⠘⡄⠱⠀⠸⡀⠄⠳⡀⠀⢳⡀⢰⠀⢸⢇⡟⠑⠦⢈⡉⠁⢼⢠⡏⣴⠟⢙⠇⠀⡇⢠⠃⢀⡴⠁⠀⠘⠀⠈⡆
⠀⠀⠀⠀⠀⠀⠀⠇⠀⠣⠀⡗⢣⡀⠘⢄⠀⢧⠀⢳⡟⠛⠙⣧⣧⣠⣄⣀⣠⢿⣶⠁⠀⠸⡀⠀⠓⠚⢴⣋⣠⠔⠀⠀⠀⠀⠁
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠧⡤⠙⢤⡈⣦⡼⠀⠀⠧⢶⠚⡇⠈⠁⠈⠃⠀⡰⢿⣄⠀⠀⠑⢤⣀⠀⠀⠀⠈⠁⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
///////////////////////////////////////////////////*/

import { Auth } from "./libraries/Auth.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";
import { ERC20 } from "solmate/tokens/ERC20.sol";
import { IERC4626 } from "./interfaces/IERC4626.sol";
import { WETH } from "solmate/tokens/WETH.sol";
import { MevEthErrors } from "./interfaces/Errors.sol";
import { IStakingModule } from "./interfaces/IStakingModule.sol";
import { IERC20Burnable } from "./interfaces/IERC20Burnable.sol";
import { ITinyMevEth } from "./interfaces/ITinyMevEth.sol";

/// @title MevEth
/// @author CommodityStream, Inc.
/// @dev Contract that allows deposit of ETH, for a Liquid Staking Receipt (LSR) in return.
/// @dev LSR is represented through an ERC4626 token and interface.
contract MevEth is Auth, ERC20, IERC4626, ITinyMevEth {
    using SafeTransferLib for WETH;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                            Configuration Variables
    //////////////////////////////////////////////////////////////*/

    /// @notice Inidicates if staking is paused.
    bool public stakingPaused;
    /// @notice Indicates if contract is initialized.
    bool public initialized;
    /// @notice withdraw fee denominator
    uint16 internal constant feeDenominator = 10_000;
    /// @notice Timestamp when pending staking module update can be finalized.
    uint64 public pendingStakingModuleCommittedTimestamp;
    /// @notice Timestamp when pending mevEthShareVault update can be finalized.
    uint64 public pendingMevEthShareVaultCommittedTimestamp;
    /// @notice Time delay before staking module or share vault can be finalized.
    uint64 internal constant MODULE_UPDATE_TIME_DELAY = 7 days;
    /// @notice Max amount of ETH that can be deposited.
    uint128 internal constant MAX_DEPOSIT = type(uint128).max;
    /// @notice Min amount of ETH that can be deposited.
    uint128 public constant MIN_DEPOSIT = 0.01 ether;
    /// @notice Min amount of ETH that can be withdrawn via the queue.
    uint128 public MIN_WITHDRAWAL;
    /// @notice The address of the MevEthShareVault.
    address public mevEthShareVault;
    /// @notice The address of the pending MevEthShareVault when a new vault has been committed but not finalized.
    address public pendingMevEthShareVault;
    /// @notice The staking module used to stake Ether.
    IStakingModule public stakingModule;
    /// @notice The pending staking module when a new module has been committed but not finalized.
    IStakingModule public pendingStakingModule;
    /// @notice WETH Implementation used by MevEth.
    WETH public immutable WETH9;
    /// @notice Last rewards payment by block number
    uint256 internal lastRewards;
    /// @notice Struct used to accounting the ETH staked within MevEth.
    Fraction public fraction;
    /// @notice The percent out of 1000 crETH2 can be redeemed for as mevEth
    uint256 public constant CREAM_TO_MEV_ETH_PERCENT = 1130;
    /// @notice The canonical address of the crETH2 address
    address public constant creamToken = 0x49D72e3973900A195A155a46441F0C08179FdB64;
    /// @notice Sandwich protection mapping of last user deposits by block number
    mapping(address => uint256) lastDeposit;
    /// @notice Deposited validators mapping to prevent double deposits
    mapping(bytes => bool) depositedValidators;

    /// @notice Central struct used for share accounting + math.
    /// @custom:field elastic   Represents total amount of staked ether, including rewards accrued / slashed.
    /// @custom:field base      Represents claims to ownership of the staked ether.
    struct Fraction {
        uint128 elastic;
        uint128 base;
    }

    /*//////////////////////////////////////////////////////////////
                                Setup
    //////////////////////////////////////////////////////////////*/

    /// @notice Construction creates mevETH token, sets authority and weth address.
    /// @dev Pending staking module and committed timestamp will both be zero on deployment.
    /// @param authority Address of the controlling admin authority.
    /// @param weth Address of the WETH contract to use for deposits.
    constructor(address authority, address weth) Auth(authority) ERC20("Mev Liquid Staking Receipt", "mevETH", 18) {
        WETH9 = WETH(payable(weth));
        MIN_WITHDRAWAL = MIN_DEPOSIT;
    }

    /// @notice Calculate the needed Ether buffer required when creating a new validator.
    /// @return uint256 The required Ether buffer.
    function calculateNeededEtherBuffer() public view returns (uint256) {
        unchecked {
            return max(withdrawalAmountQueued, (stakingModule.VALIDATOR_DEPOSIT_SIZE() / 100) * 90);
        }
    }

    /*//////////////////////////////////////////////////////////////
                            Admin Control Panel
    //////////////////////////////////////////////////////////////*/

    /// @notice Event emitted when the MevEth is successfully initialized.
    event MevEthInitialized(address indexed mevEthShareVault, address indexed stakingModule);

    /// @notice Initializes the MevEth contract, setting the staking module and share vault addresses.
    /// @param initialShareVault The initial share vault set during initialization.
    /// @param initialStakingModule The initial staking module set during initialization.
    /// @dev This function can only be called once and is protected by the onlyAdmin modifier.
    function init(address initialShareVault, address initialStakingModule) external onlyAdmin {
        // Revert if the initial share vault or staking module is the zero address.
        if (initialShareVault == address(0)) {
            revert MevEthErrors.ZeroAddress();
        }

        if (initialStakingModule == address(0)) {
            revert MevEthErrors.ZeroAddress();
        }

        // Revert if the contract has already been initialized.
        if (initialized) {
            revert MevEthErrors.AlreadyInitialized();
        }

        // Update state variables and emit event to notify offchain listeners that the contract has been initialized.
        initialized = true;
        mevEthShareVault = initialShareVault;
        stakingModule = IStakingModule(initialStakingModule);
        emit MevEthInitialized(initialShareVault, initialStakingModule);
    }

    /// @notice Emitted when staking is paused.
    event StakingPaused();
    /// @notice Emitted when staking is unpaused.
    event StakingUnpaused();

    /// @notice Ensures that staking is not paused when invoking a specific function.
    /// @dev This check is used on the createValidator, deposit and mint functions.
    function _stakingUnpaused() internal view {
        if (stakingPaused) revert MevEthErrors.StakingPaused();
    }

    /// @notice Pauses staking on the MevEth contract.
    /// @dev This function is only callable by addresses with the admin role.
    function pauseStaking() external onlyAdmin {
        stakingPaused = true;
        emit StakingPaused();
    }

    /// @notice Unauses staking on the MevEth contract.
    /// @dev This function is only callable by addresses with the admin role.
    function unpauseStaking() external onlyAdmin {
        stakingPaused = false;
        emit StakingUnpaused();
    }

    /// @notice Event emitted when a new staking module is committed.
    ///   The MODULE_UPDATE_TIME_DELAY must elapse before the staking module update can be finalized.
    event StakingModuleUpdateCommitted(address indexed oldModule, address indexed pendingModule, uint64 indexed eligibleForFinalization);

    /// @notice Event emitted when a new staking module is finalized.
    event StakingModuleUpdateFinalized(address indexed oldModule, address indexed newModule);

    /// @notice Event emitted when a new pending module update is canceled.
    event StakingModuleUpdateCanceled(address indexed oldModule, address indexed pendingModule);

    /// @notice Starts the process to update the staking module.
    ///   To finalize the update, the MODULE_UPDATE_TIME_DELAY must elapse
    ///   and thefinalizeUpdateStakingModule function must be called.
    /// @param newModule The new staking module.
    /// @dev This function is only callable by addresses with the admin role.
    function commitUpdateStakingModule(IStakingModule newModule) external onlyAdmin {
        if (address(newModule) == address(0)) {
            revert MevEthErrors.InvalidPendingStakingModule();
        }

        pendingStakingModule = newModule;
        pendingStakingModuleCommittedTimestamp = uint64(block.timestamp);
        emit StakingModuleUpdateCommitted(address(stakingModule), address(newModule), uint64(block.timestamp + MODULE_UPDATE_TIME_DELAY));
    }

    /// @notice Finalizes the staking module update if a pending staking module exists.
    /// @dev This function is only callable by addresses with the admin role.
    function finalizeUpdateStakingModule() external onlyAdmin {
        // Revert if there is no pending staking module or if the the staking module finalization is premature.
        uint64 committedTimestamp = pendingStakingModuleCommittedTimestamp;
        if (address(pendingStakingModule) == address(0) || committedTimestamp == 0) {
            revert MevEthErrors.InvalidPendingStakingModule();
        }

        if (uint64(block.timestamp) < committedTimestamp + MODULE_UPDATE_TIME_DELAY) {
            revert MevEthErrors.PrematureStakingModuleUpdateFinalization();
        }

        // Emit an event to notify offchain listeners that the staking module has been finalized.
        emit StakingModuleUpdateFinalized(address(stakingModule), address(pendingStakingModule));

        // Update the staking module
        stakingModule = pendingStakingModule;

        // Set the pending staking module variables to zero.
        pendingStakingModule = IStakingModule(address(0));
        pendingStakingModuleCommittedTimestamp = 0;
    }

    /// @notice Cancels a pending staking module update.
    /// @dev This function is only callable by addresses with the admin role.
    function cancelUpdateStakingModule() external onlyAdmin {
        // Revert if there is no pending staking module.
        if (address(pendingStakingModule) == address(0) || pendingStakingModuleCommittedTimestamp == 0) {
            revert MevEthErrors.InvalidPendingStakingModule();
        }

        // Emit an event to notify offchain listeners that the staking module has been canceled.
        emit StakingModuleUpdateCanceled(address(stakingModule), address(pendingStakingModule));

        // Set the pending staking module variables to zero.
        pendingStakingModule = IStakingModule(address(0));
        pendingStakingModuleCommittedTimestamp = 0;
    }

    /// @notice Event emitted when a new share vault is committed. To finalize the update, the MODULE_UPDATE_TIME_DELAY must elapse and the
    ///         finalizeUpdateMevEthShareVault function must be called.
    event MevEthShareVaultUpdateCommitted(address indexed oldVault, address indexed pendingVault, uint64 indexed eligibleForFinalization);
    /// @notice Event emitted when a new share vault is finalized.
    event MevEthShareVaultUpdateFinalized(address indexed oldVault, address indexed newVault);
    /// @notice Event emitted when a new pending share vault update is canceled.
    event MevEthShareVaultUpdateCanceled(address indexed oldVault, address indexed newVault);

    /// @notice Starts the process to update the share vault. To finalize the update, the MODULE_UPDATE_TIME_DELAY must elapse and the
    ///         finalizeUpdateStakingModule function must be called.
    /// @param newMevEthShareVault The new share vault
    /// @dev This function is only callable by addresses with the admin role
    function commitUpdateMevEthShareVault(address newMevEthShareVault) external onlyAdmin {
        if (newMevEthShareVault == address(0)) {
            revert MevEthErrors.ZeroAddress();
        }
        pendingMevEthShareVault = newMevEthShareVault;
        pendingMevEthShareVaultCommittedTimestamp = uint64(block.timestamp);
        emit MevEthShareVaultUpdateCommitted(mevEthShareVault, newMevEthShareVault, uint64(block.timestamp + MODULE_UPDATE_TIME_DELAY));
    }

    /// @notice Finalizes the share vault update if a pending share vault exists.
    /// @dev This function is only callable by addresses with the admin role.
    function finalizeUpdateMevEthShareVault() external onlyAdmin {
        // Revert if there is no pending share vault or if the the share vault finalization is premature.
        uint64 committedTimestamp = pendingMevEthShareVaultCommittedTimestamp;
        if (pendingMevEthShareVault == address(0) || committedTimestamp == 0) {
            revert MevEthErrors.InvalidPendingMevEthShareVault();
        }

        if (uint64(block.timestamp) < committedTimestamp + MODULE_UPDATE_TIME_DELAY) {
            revert MevEthErrors.PrematureMevEthShareVaultUpdateFinalization();
        }

        /// @custom:: When finalizing the update to the MevEthShareVault, make sure to grant any remaining rewards from the existing share vault.
        // Emit an event to notify offchain listeners that the share vault has been finalized.
        emit MevEthShareVaultUpdateFinalized(mevEthShareVault, address(pendingMevEthShareVault));

        // Update the mev share vault
        mevEthShareVault = pendingMevEthShareVault;

        // Set the pending vault variables to zero
        pendingMevEthShareVault = address(0);
        pendingMevEthShareVaultCommittedTimestamp = 0;
    }

    /// @notice Cancels a pending share vault update.
    /// @dev This function is only callable by addresses with the admin role.
    function cancelUpdateMevEthShareVault() external onlyAdmin {
        // Revert if there is no pending share vault.
        if (pendingMevEthShareVault == address(0) || pendingMevEthShareVaultCommittedTimestamp == 0) {
            revert MevEthErrors.InvalidPendingMevEthShareVault();
        }
        // Emit an event to notify offchain listeners that the share vault has been canceled.
        emit MevEthShareVaultUpdateCanceled(mevEthShareVault, pendingMevEthShareVault);

        //Set the pending vault variables to zero
        pendingMevEthShareVault = address(0);
        pendingMevEthShareVaultCommittedTimestamp = 0;
    }

    /*//////////////////////////////////////////////////////////////
                            Registry For Validators
    //////////////////////////////////////////////////////////////*/

    /// @notice Event emitted when a new validator is created
    event ValidatorCreated(address indexed stakingModule, IStakingModule.ValidatorData newValidator);

    /// @notice This function passes through the needed Ether to the Staking module, and the assosiated credentials with it
    /// @param newData The data needed to create a new validator
    /// @dev This function is only callable by addresses with the operator role and if staking is unpaused
    function createValidator(IStakingModule.ValidatorData calldata newData, bytes32 latestDepositRoot) external onlyOperator {
        // check if staking is paused
        _stakingUnpaused();
        // check validator does not already exist
        if (depositedValidators[newData.pubkey]) revert MevEthErrors.AlreadyDeposited();
        // set validator deposited to true
        depositedValidators[newData.pubkey] = true;
        IStakingModule _stakingModule = stakingModule;
        // check withdrawal address is correct
        if (address(_stakingModule) != address(uint160(uint256(newData.withdrawal_credentials)))) revert MevEthErrors.IncorrectWithdrawalCredentials();
        // Determine how big deposit is for the validator
        uint256 depositSize = _stakingModule.VALIDATOR_DEPOSIT_SIZE();

        if (address(this).balance < depositSize + calculateNeededEtherBuffer()) {
            revert MevEthErrors.NotEnoughEth();
        }

        // Deposit the Ether into the staking contract
        _stakingModule.deposit{ value: depositSize }(newData, latestDepositRoot);

        emit ValidatorCreated(address(_stakingModule), newData);
    }

    /// @notice Event emitted when rewards are granted.
    event Rewards(address sender, uint256 amount);

    /// @notice Grants rewards updating the fraction.elastic.
    /// @dev called from validator rewards updates
    function grantRewards() external payable {
        if (!(msg.sender == address(stakingModule) || msg.sender == mevEthShareVault)) revert MevEthErrors.UnAuthorizedCaller();
        if (msg.value == 0) revert MevEthErrors.ZeroValue();

        fraction.elastic += uint128(msg.value);
        lastRewards = block.number;
        emit Rewards(msg.sender, msg.value);
    }

    /// @notice  Emitted when validator withdraw funds are received.
    event ValidatorWithdraw(address sender, uint256 amount);

    /// @notice Allows the MevEthShareVault or the staking module to withdraw validator funds from the contract.
    /// @dev Before updating the fraction, the withdrawal queue is processed, which pays out any pending withdrawals.
    /// @dev This function is only callable by the MevEthShareVault or the staking module.
    function grantValidatorWithdraw() external payable {
        // Check that the sender is the staking module or the MevEthShareVault.
        if (!(msg.sender == address(stakingModule) || msg.sender == mevEthShareVault)) revert MevEthErrors.InvalidSender();

        // Check that the value is not zero
        if (msg.value != 32 ether) {
            revert MevEthErrors.WrongWithdrawAmount();
        }

        // Emit an event to notify offchain listeners that a validator has withdrawn funds.
        emit ValidatorWithdraw(msg.sender, msg.value);

        // Register our exit with the staking module
        stakingModule.registerExit();
    }

    /*//////////////////////////////////////////////////////////////
                            WITHDRAWAL QUEUE
    //////////////////////////////////////////////////////////////*/

    /// @notice Struct representing a withdrawal ticket which is added to the withdrawal queue.
    /// @custom:field claimed               True if this receiver has received ticket funds.
    /// @custom:field receiver              The receiever of the ETH specified in the WithdrawalTicket.
    /// @custom:field amount                The amount of ETH to send to the receiver when the ticket is processed.
    /// @custom:field accumulatedAmount     Keep a running sum of all requested ETH
    struct WithdrawalTicket {
        bool claimed;
        address receiver;
        uint128 amount;
        uint128 accumulatedAmount;
    }

    /// @notice Event emitted when a withdrawal ticket is added to the queue.
    event WithdrawalQueueOpened(address indexed recipient, uint256 indexed withdrawalId, uint256 assets);
    event WithdrawalQueueClosed(address indexed recipient, uint256 indexed withdrawalId, uint256 assets);

    /// @notice The length of the withdrawal queue.
    uint256 public queueLength;

    /// @notice  mark the latest withdrawal request that was finalised
    uint256 public requestsFinalisedUntil;

    /// @notice Withdrawal amount queued
    uint256 public withdrawalAmountQueued;

    /// @notice The mapping representing the withdrawal queue.
    /// @dev The index in the queue is the key, and the value is the WithdrawalTicket.
    mapping(uint256 ticketNumber => WithdrawalTicket ticket) public withdrawalQueue;

    /// @notice Claim Finalised Withdrawal Ticket
    /// @param withdrawalId Unique ID of the withdrawal ticket
    function claim(uint256 withdrawalId) external {
        if (withdrawalId > requestsFinalisedUntil) revert MevEthErrors.NotFinalised();
        WithdrawalTicket storage ticket = withdrawalQueue[withdrawalId];
        if (ticket.claimed) revert MevEthErrors.AlreadyClaimed();
        withdrawalQueue[withdrawalId].claimed = true;
        withdrawalAmountQueued -= uint256(ticket.amount);
        emit WithdrawalQueueClosed(ticket.receiver, withdrawalId, uint256(ticket.amount));
        WETH9.deposit{ value: uint256(ticket.amount) }();
        WETH9.safeTransfer(ticket.receiver, uint256(ticket.amount));
    }

    /// @notice Processes the withdrawal queue, reserving any pending withdrawals with the contract's available balance.
    function processWithdrawalQueue(uint256 newRequestsFinalisedUntil) external onlyOperator {
        if (newRequestsFinalisedUntil > queueLength) revert MevEthErrors.IndexExceedsQueueLength();
        uint256 balance = address(this).balance;
        if (withdrawalAmountQueued >= balance) revert MevEthErrors.NotEnoughEth();
        uint256 available = balance - withdrawalAmountQueued;

        uint256 finalised = requestsFinalisedUntil;
        if (newRequestsFinalisedUntil < finalised) revert MevEthErrors.AlreadyFinalised();

        uint256 delta = uint256(withdrawalQueue[newRequestsFinalisedUntil].accumulatedAmount - withdrawalQueue[finalised].accumulatedAmount);
        if (available < delta) revert MevEthErrors.NotEnoughEth();

        requestsFinalisedUntil = newRequestsFinalisedUntil;
        withdrawalAmountQueued += delta;
    }

    function setMinWithdrawal(uint128 newMinimum) public onlyAdmin {
        MIN_WITHDRAWAL = newMinimum;
    }

    /*//////////////////////////////////////////////////////////////
                            ERC4626 Support
    //////////////////////////////////////////////////////////////*/
    /// @notice The underlying asset of the mevEth contract
    /// @return assetTokenAddress The address of the asset token
    function asset() external view returns (address assetTokenAddress) {
        assetTokenAddress = address(WETH9);
    }

    /// @notice The total amount of assets controlled by the mevEth contract
    /// @return totalManagedAssets The amount of eth controlled by the mevEth contract
    function totalAssets() external view returns (uint256 totalManagedAssets) {
        // Should return the total amount of Ether managed by the contract
        totalManagedAssets = uint256(fraction.elastic);
    }

    /// @notice Function to convert a specified amount of assets to shares based on the elastic and base.
    /// @param assets The amount of assets to convert to shares
    /// @return shares The value of the given assets in shares
    function convertToShares(uint256 assets) public view returns (uint256 shares) {
        // So if there are no shares, then they will mint 1:1 with assets
        // Otherwise, shares will mint proportional to the amount of assets
        if ((uint256(fraction.elastic) == 0) || (uint256(fraction.base) == 0)) {
            shares = assets;
        } else {
            shares = (assets * uint256(fraction.base)) / uint256(fraction.elastic);
        }
    }

    /// @notice Function to convert a specified amount of shares to assets based on the elastic and base.
    /// @param shares The amount of shares to convert to assets
    /// @return assets The value of the given shares in assets
    function convertToAssets(uint256 shares) public view returns (uint256 assets) {
        // So if there are no shares, then they will mint 1:1 with assets
        // Otherwise, shares will mint proportional to the amount of assets
        if (uint256(fraction.elastic) == 0 || uint256(fraction.base) == 0) {
            assets = shares;
        } else {
            assets = (shares * uint256(fraction.elastic)) / uint256(fraction.base);
        }
    }

    /// @notice Function to indicate the maximum deposit possible.
    /// @return maxAssets The maximum amount of assets that can be deposited.
    function maxDeposit(address) external view returns (uint256 maxAssets) {
        // If staking is paused, then no deposits can be made
        if (stakingPaused) {
            return 0;
        }
        // No practical limit on deposit for Ether
        maxAssets = uint256(MAX_DEPOSIT);
    }

    /// @notice Function to simulate the amount of shares that would be minted for a given deposit at the current ratio.
    /// @param assets The amount of assets that would be deposited
    /// @return shares The amount of shares that would be minted, *under ideal conditions* only
    function previewDeposit(uint256 assets) external view returns (uint256 shares) {
        return convertToShares(assets);
    }

    /// @notice internal deposit function to process Weth or Eth deposits
    /// @param receiver The address user whom should receive the mevEth out
    /// @param assets The amount of assets to deposit
    /// @param shares The amount of shares that should be minted
    function _deposit(address receiver, uint256 assets, uint256 shares) internal {
        // If the deposit is less than the minimum deposit, revert
        if (assets < MIN_DEPOSIT) revert MevEthErrors.DepositTooSmall();

        fraction.elastic += uint128(assets);
        fraction.base += uint128(shares);

        // Update last deposit block for the user recorded for sandwich protection
        lastDeposit[msg.sender] = block.number;
        lastDeposit[receiver] = block.number;

        if (msg.value == 0) {
            WETH9.safeTransferFrom(msg.sender, address(this), assets);
            WETH9.withdraw(assets);
        } else {
            if (msg.value != assets) revert MevEthErrors.WrongDepositAmount();
        }

        // Mint MevEth shares to the receiver
        _mint(receiver, shares);

        // Emit the deposit event to notify offchain listeners that a deposit has occured
        emit Deposit(msg.sender, receiver, assets, shares);
    }

    /// @notice Function to deposit assets into the mevEth contract
    /// @param assets The amount of WETH which should be deposited
    /// @param receiver The address user whom should receive the mevEth out
    /// @return shares The amount of shares minted
    function deposit(uint256 assets, address receiver) external payable returns (uint256 shares) {
        _stakingUnpaused();

        // Convert the assets to shares and update the fraction elastic and base
        shares = convertToShares(assets);

        // Deposit the assets
        _deposit(receiver, assets, shares);
    }

    /// @notice Function to indicate the maximum amount of shares that can be minted at the current ratio.
    /// @return maxShares The maximum amount of shares that can be minted
    function maxMint(address) external view returns (uint256 maxShares) {
        // If staking is paused, no shares can be minted
        if (stakingPaused) {
            return 0;
        }
        // No practical limit on mint for Ether
        return MAX_DEPOSIT;
    }

    /// @notice Function to simulate the amount of assets that would be required to mint a given amount of shares at the current ratio.
    /// @param shares The amount of shares that would be minted
    /// @return assets The amount of assets that would be required, *under ideal conditions* only
    function previewMint(uint256 shares) external view returns (uint256 assets) {
        return convertToAssets(shares);
    }

    /// @notice Function to mint shares of the mevEth contract
    /// @param shares The amount of shares that should be minted
    /// @param receiver The address user whom should receive the mevEth out
    /// @return assets The amount of assets deposited
    function mint(uint256 shares, address receiver) external payable returns (uint256 assets) {
        _stakingUnpaused();

        // Convert the shares to assets and update the fraction elastic and base
        assets = convertToAssets(shares);

        // Deposit the assets
        _deposit(receiver, assets, shares);
    }

    /// @notice Function to indicate the maximum amount of assets that can be withdrawn at the current state.
    /// @param owner The address in question of who would be withdrawing
    /// @return maxAssets The maximum amount of assets that can be withdrawn
    function maxWithdraw(address owner) external view returns (uint256 maxAssets) {
        // Withdrawal is either their maximum balance, or the internal buffer
        maxAssets = min(address(this).balance, convertToAssets(balanceOf[owner]));
    }

    /// @notice Function to simulate the amount of shares that would be allocated for a specified amount of assets.
    /// @param assets The amount of assets that would be withdrawn
    /// @return shares The amount of shares that would be burned, *under ideal conditions* only
    function previewWithdraw(uint256 assets) external view returns (uint256 shares) {
        // withdraw fee fixed at 0.01%
        uint256 fee = assets / uint256(feeDenominator);
        shares = convertToShares(assets + fee);
    }

    ///@notice Function to withdraw assets from the mevEth contract
    /// @param useQueue Flag whether to use the withdrawal queue
    /// @param receiver The address user whom should receive the mevEth out
    /// @param owner The address of the owner of the mevEth
    /// @param assets The amount of assets that should be withdrawn
    /// @param shares shares that will be burned
    function _withdraw(bool useQueue, address receiver, address owner, uint256 assets, uint256 shares) internal {
        // If withdraw is less than the minimum deposit / withdraw amount, revert
        if (assets < MIN_WITHDRAWAL) revert MevEthErrors.WithdrawTooSmall();
        // Sandwich protection
        uint256 blockNumber = block.number;

        if (((blockNumber - lastDeposit[msg.sender]) == 0 || (blockNumber - lastDeposit[owner] == 0)) && (blockNumber - lastRewards) == 0) {
            revert MevEthErrors.SandwichProtection();
        }

        _updateAllowance(owner, shares);

        // Update the elastic and base
        fraction.elastic -= uint128(assets);
        fraction.base -= uint128(shares);

        // Burn the shares and emit a withdraw event for offchain listeners to know that a withdraw has occured
        _burn(owner, shares);

        uint256 availableBalance = address(this).balance - withdrawalAmountQueued; // available balance will be adjusted
        uint256 amountToSend = assets;
        if (availableBalance < assets) {
            if (!useQueue) revert MevEthErrors.NotEnoughEth();
            // Available balance is sent, and the remainder must be withdrawn via the queue
            uint256 amountOwed = assets - availableBalance;
            ++queueLength;
            withdrawalQueue[queueLength] = WithdrawalTicket({
                claimed: false,
                receiver: receiver,
                amount: uint128(amountOwed),
                accumulatedAmount: withdrawalQueue[queueLength - 1].accumulatedAmount + uint128(amountOwed)
            });
            emit WithdrawalQueueOpened(receiver, queueLength, amountOwed);
            amountToSend = availableBalance;
        }
        if (amountToSend != 0) {
            // As with ERC4626, we log assets and shares as if there is no queue, and everything has been withdrawn
            // as this most closely resembles what is happened
            emit Withdraw(msg.sender, owner, receiver, assets, shares);

            WETH9.deposit{ value: amountToSend }();
            WETH9.safeTransfer(receiver, amountToSend);
        }
    }

    /// @dev internal function to update allowance for withdraws if necessary
    /// @param owner owner of tokens
    /// @param shares amount of shares to update
    function _updateAllowance(address owner, uint256 shares) internal {
        uint256 allowed = allowance[owner][msg.sender];
        if (owner != msg.sender) {
            if (allowed < shares) revert MevEthErrors.TransferExceedsAllowance();
            if (allowed != type(uint256).max) {
                unchecked {
                    allowance[owner][msg.sender] -= shares;
                }
            }
        }
    }

    /// @notice Withdraw assets if balance is available
    /// @param assets The amount of assets that should be withdrawn
    /// @param receiver The address user whom should receive the mevEth out
    /// @param owner The address of the owner of the mevEth
    /// @return shares The amount of shares burned
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares) {
        // withdraw fee fixed at 0.01%
        uint256 fee = assets / uint256(feeDenominator);
        // Convert the assets to shares and check if the owner has the allowance to withdraw the shares.
        shares = convertToShares(assets + fee);

        // Withdraw the assets from the MevEth contract
        _withdraw(false, receiver, owner, assets, shares);
    }

    /// @notice Withdraw assets or open queue ticket for claim depending on balance available
    /// @param assets The amount of assets that should be withdrawn
    /// @param receiver The address user whom should receive the mevEth out
    /// @param owner The address of the owner of the mevEth
    /// @return shares The amount of shares burned
    function withdrawQueue(uint256 assets, address receiver, address owner) external returns (uint256 shares) {
        // withdraw fee fixed at 0.01%
        uint256 fee = assets / uint256(feeDenominator);
        // last shareholder has no fee
        if ((fraction.elastic - assets) == 0) fee = 0;
        // Convert the assets to shares and check if the owner has the allowance to withdraw the shares.
        shares = convertToShares(assets + fee);

        // Withdraw the assets from the MevEth contract
        _withdraw(true, receiver, owner, assets, shares);
    }

    ///@notice Function to simulate the maximum amount of shares that can be redeemed by the owner.
    /// @param owner The address in question of who would be redeeming their shares
    /// @return maxShares The maximum amount of shares they could redeem
    function maxRedeem(address owner) external view returns (uint256 maxShares) {
        maxShares = min(convertToShares(address(this).balance), balanceOf[owner]);
    }

    /// @notice Function to simulate the amount of assets that would be withdrawn for a specified amount of shares.
    /// @param shares The amount of shares that would be burned
    /// @return assets The amount of assets that would be withdrawn, *under ideal conditions* only
    function previewRedeem(uint256 shares) external view returns (uint256 assets) {
        // withdraw fee fixed at 0.01%
        uint256 fee = shares / uint256(feeDenominator);
        assets = convertToAssets(shares - fee);
    }

    /// @notice Function to redeem shares from the mevEth contract
    /// @param shares The amount of shares that should be burned
    /// @param receiver The address user whom should receive the wETH out
    /// @param owner The address of the owner of the mevEth
    /// @return assets The amount of assets withdrawn
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets) {
        // withdraw fee fixed at 0.01%
        uint256 fee = shares / uint256(feeDenominator);
        // last shareholder has no fee
        if ((totalSupply - shares) == 0) fee = 0;
        // Convert the shares to assets and check if the owner has the allowance to withdraw the shares.
        assets = convertToAssets(shares - fee);

        // Withdraw the assets from the MevEth contract
        _withdraw(false, receiver, owner, assets, shares);
    }

    /*//////////////////////////////////////////////////////////////
                            Utility Functions
    //////////////////////////////////////////////////////////////*/

    /// @dev Returns the largest of two numbers.
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /// @dev Returns the smallest of two numbers.
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /*//////////////////////////////////////////////////////////////
             Special CreamEth2 redeem (from initial migration)
     //////////////////////////////////////////////////////////////*/

    /// @notice Redeem Cream staked eth tokens for mevETH at a fixed ratio
    /// @param creamAmount The amount of Cream tokens to redeem
    function redeemCream(uint256 creamAmount) external {
        _stakingUnpaused();
        if (creamAmount == 0) revert MevEthErrors.ZeroValue();

        // Calculate the equivalent mevETH to be redeemed based on the ratio
        uint256 assets = creamAmount * uint256(CREAM_TO_MEV_ETH_PERCENT) / 1000;
        if (assets < MIN_DEPOSIT) revert MevEthErrors.DepositTooSmall();

        // Convert the shares to assets and update the fraction elastic and base
        uint256 shares = convertToShares(assets);

        fraction.elastic += uint128(assets);
        fraction.base += uint128(shares);

        // Burn CreamEth2 tokens
        IERC20Burnable(creamToken).burnFrom(msg.sender, creamAmount);

        // Mint the equivalent mevETH
        _mint(msg.sender, shares);

        // Emit event
        emit CreamRedeemed(msg.sender, creamAmount, shares);
    }

    // Event emitted when Cream tokens are redeemed for mevETH
    event CreamRedeemed(address indexed redeemer, uint256 creamAmount, uint256 mevEthAmount);

    /// @dev Only Weth withdraw is defined for the behaviour. Deposits should be directed to deposit / mint. Rewards via grantRewards and validator withdraws
    /// via grantValidatorWithdraw.
    receive() external payable {
        if (msg.sender != address(WETH9)) revert MevEthErrors.InvalidSender();
    }

    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        uint256 lastDepositFrom = lastDeposit[msg.sender];
        if (lastDepositFrom > lastDeposit[to]) {
            lastDeposit[to] = lastDepositFrom;
        }

        return super.transfer(to, amount);
    }

    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        uint256 lastDepositFrom = lastDeposit[from];
        if (lastDepositFrom > lastDeposit[to]) {
            lastDeposit[to] = lastDepositFrom;
        }

        return super.transferFrom(from, to, amount);
    }
}

File 2 of 11 : Auth.sol
/// SPDX-License-Identifier: SSPL-1.-0

/**
 * @custom:org.protocol='mevETH LST Protocol'
 * @custom:org.security='mailto:[email protected]'
 * @custom:org.vcs-commit=$GIT_COMMIT_SHA
 * @custom:org.vendor='CommodityStream, Inc'
 * @custom:org.schema-version="1.0"
 * @custom.org.encryption="manifoldfinance.com/.well-known/pgp-key.asc"
 * @custom:org.preferred-languages="en"
 */

pragma solidity ^0.8.19;

contract Auth {
    error Unauthorized();
    error AlreadySet();
    error NoAdmin();

    event AdminAdded(address indexed newAdmin);
    event AdminDeleted(address indexed oldAdmin);
    event OperatorAdded(address indexed newOperator);
    event OperatorDeleted(address indexed oldOperator);

    // admin counter (assuming 255 admins to be max)
    uint8 adminsCounter;

    // Keeps track of all operators
    mapping(address => bool) public operators;

    // Keeps track of all admins
    mapping(address => bool) public admins;

    /**
     * @notice This constructor sets the initialAdmin address as an admin and operator.
     * @dev The adminsCounter is incremented unchecked.
     */
    constructor(address initialAdmin) {
        admins[initialAdmin] = true;
        unchecked {
            ++adminsCounter;
        }
        operators[initialAdmin] = true;
    }

    /*//////////////////////////////////////////////////////////////
                           Access Control Modifiers
    //////////////////////////////////////////////////////////////*/

    modifier onlyAdmin() {
        if (!admins[msg.sender]) {
            revert Unauthorized();
        }
        _;
    }

    modifier onlyOperator() {
        if (!operators[msg.sender]) {
            revert Unauthorized();
        }
        _;
    }

    /*//////////////////////////////////////////////////////////////
                           Maintenance Functions
    //////////////////////////////////////////////////////////////*/
    /**
     * @notice addAdmin() function allows an admin to add a new admin to the contract.
     * @dev This function is only accessible to the existing admins and requires the address of the new admin.
     * If the new admin is already set, the function will revert. Otherwise, the adminsCounter will be incremented and the new admin will be added to the admins
     * mapping. An AdminAdded event will be emitted.
     */
    function addAdmin(address newAdmin) external onlyAdmin {
        if (admins[newAdmin]) revert AlreadySet();
        ++adminsCounter;
        admins[newAdmin] = true;
        emit AdminAdded(newAdmin);
    }

    /**
     * @notice Deletes an admin from the list of admins.
     * @dev Only admins can delete other admins. If the adminsCounter is 0, the transaction will revert.
     */
    function deleteAdmin(address oldAdmin) external onlyAdmin {
        if (!admins[oldAdmin]) revert AlreadySet();
        --adminsCounter;
        if (adminsCounter == 0) revert NoAdmin();
        admins[oldAdmin] = false;
        emit AdminDeleted(oldAdmin);
    }

    /**
     * @notice Adds a new operator to the list of operators
     * @dev Only the admin can add a new operator
     * @param newOperator The address of the new operator
     */
    function addOperator(address newOperator) external onlyAdmin {
        if (operators[newOperator]) revert AlreadySet();
        operators[newOperator] = true;
        emit OperatorAdded(newOperator);
    }

    function deleteOperator(address oldOperator) external onlyAdmin {
        if (!operators[oldOperator]) revert AlreadySet();
        operators[oldOperator] = false;
        emit OperatorDeleted(oldOperator);
    }
}

File 3 of 11 : SafeTransferLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // We'll write our calldata to this slot below, but restore it later.
            let memPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(4, from) // Append the "from" argument.
            mstore(36, to) // Append the "to" argument.
            mstore(68, amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because that's the total length of our calldata (4 + 32 * 3)
                // Counterintuitively, this call() must be positioned after the or() in the
                // surrounding and() because and() evaluates its arguments from right to left.
                call(gas(), token, 0, 0, 100, 0, 32)
            )

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, memPointer) // Restore the memPointer.
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // We'll write our calldata to this slot below, but restore it later.
            let memPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(0, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(4, to) // Append the "to" argument.
            mstore(36, amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because that's the total length of our calldata (4 + 32 * 2)
                // Counterintuitively, this call() must be positioned after the or() in the
                // surrounding and() because and() evaluates its arguments from right to left.
                call(gas(), token, 0, 0, 68, 0, 32)
            )

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, memPointer) // Restore the memPointer.
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        assembly {
            // We'll write our calldata to this slot below, but restore it later.
            let memPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(0, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(4, to) // Append the "to" argument.
            mstore(36, amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because that's the total length of our calldata (4 + 32 * 2)
                // Counterintuitively, this call() must be positioned after the or() in the
                // surrounding and() because and() evaluates its arguments from right to left.
                call(gas(), token, 0, 0, 68, 0, 32)
            )

            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, memPointer) // Restore the memPointer.
        }

        require(success, "APPROVE_FAILED");
    }
}

File 4 of 11 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

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

    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is < 0.5 we return zero. This happens when
            // x <= floor(log(0.5e18) * 1e18) ~ -42e18
            if (x <= -42139678854452767551) return 0;

            // When the result is > (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 (x >= 135305999368893231589) revert("EXP_OVERFLOW");

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

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

    function lnWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            require(x > 0, "UNDEFINED");

            // 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.

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            int256 k = int256(log2(uint256(x))) - 96;
            x <<= uint256(159 - k);
            x = int256(uint256(x) >> 159);

            // Evaluate using a (8, 8)-term rational approximation.
            // p is made monic, we will multiply by a scale factor later.
            int256 p = x + 3273285459638523848632254066296;
            p = ((p * x) >> 96) + 24828157081833163892658089445524;
            p = ((p * x) >> 96) + 43456485725739037958740375743393;
            p = ((p * x) >> 96) - 11111509109440967052023855526967;
            p = ((p * x) >> 96) - 45023709667254063763336534515857;
            p = ((p * x) >> 96) - 14706773417378608786704636184526;
            p = p * x - (795164235651350426258249787498 << 96);

            // 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.
            int256 q = x + 5573035233440673466300451813936;
            q = ((q * x) >> 96) + 71694874799317883764090561454958;
            q = ((q * x) >> 96) + 283447036172924575727196451306956;
            q = ((q * x) >> 96) + 401686690394027663651624208769553;
            q = ((q * x) >> 96) + 204048457590392012362485061816622;
            q = ((q * x) >> 96) + 31853899698501571402653359427138;
            q = ((q * x) >> 96) + 909429971244387300277376558375;
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial is known not to have zeros in the domain.
                // No scaling required because p is already 2**96 too large.
                r := sdiv(p, q)
            }

            // r 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

            // mul s * 5e18 * 2**96, base is now 5**18 * 2**192
            r *= 1677202110996718588342820967067443963516166;
            // add ln(2) * k * 5e18 * 2**192
            r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
            // add ln(2**96 / 10**18) * 5e18 * 2**192
            r += 600920179829731861736702779321621459595472258049074101567377883020018308;
            // base conversion: mul 2**18 / 2**192
            r >>= 174;
        }
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
                revert(0, 0)
            }

            // First, divide z - 1 by the denominator and add 1.
            // We allow z - 1 to underflow if z is 0, because we multiply the
            // end result by 0 if z is zero, ensuring we return 0 if z is zero.
            z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            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.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, 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(y, 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
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function log2(uint256 x) internal pure returns (uint256 r) {
        require(x > 0, "UNDEFINED");

        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))))
            r := or(r, shl(2, lt(0xf, shr(r, x))))
            r := or(r, shl(1, lt(0x3, shr(r, x))))
            r := or(r, lt(0x1, shr(r, x)))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            // z will equal 0 if y is 0, unlike in Solidity where it will revert.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            // z will equal 0 if y is 0, unlike in Solidity where it will revert.
            z := div(x, y)
        }
    }

    /// @dev Will return 0 instead of reverting if y is zero.
    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            // Add 1 to x * y if x % y > 0.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 5 of 11 : ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 6 of 11 : IERC4626.sol
/// SPDX-License-Identifier: SSPL-1.-0

/**
 * @custom:org.protocol='mevETH LST Protocol'
 * @custom:org.security='mailto:[email protected]'
 * @custom:org.vcs-commit=$GIT_COMMIT_SHA
 * @custom:org.vendor='CommodityStream, Inc'
 * @custom:org.schema-version="1.0"
 * @custom.org.encryption="manifoldfinance.com/.well-known/pgp-key.asc"
 * @custom:org.preferred-languages="en"
 */

pragma solidity ^0.8.19;

// Also a superset of ERC20 but due to some solmate <-> OZ IERC20 nastiness this interface doesn't include it
interface IERC4626 {
    /// @return assetTokenAddress The address of the asset token
    function asset() external view returns (address assetTokenAddress);

    /// @return totalManagedAssets The amount of eth controlled by the vault
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /// @param assets The amount of assets to convert to shares
    /// @return shares The value of the given assets in shares
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /// @param shares The amount of shares to convert to assets
    /// @return assets The value of the given shares in assets
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /// @param reciever The address in question of who would be depositing, doesn't matter in this case
    /// @return maxAssets The maximum amount of assets that can be deposited
    function maxDeposit(address reciever) external view returns (uint256 maxAssets);

    /// @param assets The amount of assets that would be deposited
    /// @return shares The amount of shares that would be minted, *under ideal conditions* only
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /// @param assets The amount of WETH which should be deposited
    /// @param receiver The address user whom should recieve the mevEth out
    /// @return shares The amount of shares minted
    function deposit(uint256 assets, address receiver) external payable returns (uint256 shares);

    /// @param reciever The address in question of who would be minting, doesn't matter in this case
    /// @return maxShares The maximum amount of shares that can be minted
    function maxMint(address reciever) external view returns (uint256 maxShares);

    /// @param shares The amount of shares that would be minted
    /// @return assets The amount of assets that would be required, *under ideal conditions* only
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /// @param shares The amount of shares that should be minted
    /// @param receiver The address user whom should recieve the mevEth out
    /// @return assets The amount of assets deposited
    function mint(uint256 shares, address receiver) external payable returns (uint256 assets);

    /// @param owner The address in question of who would be withdrawing
    /// @return maxAssets The maximum amount of assets that can be withdrawn
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /// @param assets The amount of assets that would be withdrawn
    /// @return shares The amount of shares that would be burned, *under ideal conditions* only
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /// @param assets The amount of assets that should be withdrawn
    /// @param receiver The address user whom should recieve the mevEth out
    /// @param owner The address of the owner of the mevEth
    /// @return shares The amount of shares burned
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /// @param owner The address in question of who would be redeeming their shares
    /// @return maxShares The maximum amount of shares they could redeem
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /// @param shares The amount of shares that would be burned
    /// @return assets The amount of assets that would be withdrawn, *under ideal conditions* only
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /// @param shares The amount of shares that should be burned
    /// @param receiver The address user whom should recieve the wETH out
    /// @param owner The address of the owner of the mevEth
    /// @return assets The amount of assets withdrawn
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);

    /**
     * @dev Emitted when a deposit is made, either through mint or deposit
     */
    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    /**
     * @dev Emitted when a withdrawal is made, either through redeem or withdraw
     */
    event Withdraw(address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares);
}

File 7 of 11 : WETH.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

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

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

/// @notice Minimalist and modern Wrapped Ether implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/WETH.sol)
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WETH is ERC20("Wrapped Ether", "WETH", 18) {
    using SafeTransferLib for address;

    event Deposit(address indexed from, uint256 amount);

    event Withdrawal(address indexed to, uint256 amount);

    function deposit() public payable virtual {
        _mint(msg.sender, msg.value);

        emit Deposit(msg.sender, msg.value);
    }

    function withdraw(uint256 amount) public virtual {
        _burn(msg.sender, amount);

        emit Withdrawal(msg.sender, amount);

        msg.sender.safeTransferETH(amount);
    }

    receive() external payable virtual {
        deposit();
    }
}

File 8 of 11 : Errors.sol
/// SPDX-License-Identifier: SSPL-1.-0

/**
 * @custom:org.protocol='mevETH LST Protocol'
 * @custom:org.security='mailto:[email protected]'
 * @custom:org.vcs-commit=$GIT_COMMIT_SHA
 * @custom:org.vendor='CommodityStream, Inc'
 * @custom:org.schema-version="1.0"
 * @custom.org.encryption="manifoldfinance.com/.well-known/pgp-key.asc"
 * @custom:org.preferred-languages="en"
 */

pragma solidity ^0.8.19;

interface MevEthErrors {
    /// Errors
    error StakingPaused();
    error NotEnoughEth();
    error ZeroValue();
    error InvalidOperator();
    error DepositTooSmall();
    error InvalidSender();
    error PrematureStakingModuleUpdateFinalization();
    error PrematureMevEthShareVaultUpdateFinalization();
    error InvalidPendingStakingModule();
    error InvalidPendingMevEthShareVault();
    error TransferExceedsAllowance();
    error TransferFailed();
    error ZeroAddress();
    error AlreadyInitialized();
    error SendError();
    error FeesTooHigh();
    error WrongDepositAmount();
    error WrongWithdrawAmount();
    error UnAuthorizedCaller();
    error WithdrawTooSmall();
    error NotFinalised();
    error AlreadyClaimed();
    error AlreadyFinalised();
    error IndexExceedsQueueLength();
    error DepositWasFrontrun();
    error SandwichProtection();
    error NonZeroVaultBalance();
    error AlreadyDeposited();
    error IncorrectWithdrawalCredentials();
}

File 9 of 11 : IStakingModule.sol
/// SPDX-License-Identifier: SSPL-1.-0

/**
 * @custom:org.protocol='mevETH LST Protocol'
 * @custom:org.security='mailto:[email protected]'
 * @custom:org.vcs-commit=$GIT_COMMIT_SHA
 * @custom:org.vendor='CommodityStream, Inc'
 * @custom:org.schema-version="1.0"
 * @custom.org.encryption="manifoldfinance.com/.well-known/pgp-key.asc"
 * @custom:org.preferred-languages="en"
 */

pragma solidity ^0.8.19;

interface IStakingModule {
    /**
     * @dev Structure for passing information about the validator deposit data.
     * @param operator - address of the operator.
     * @param pubkey - BLS public key of the validator, generated by the operator.
     * @param withdrawal_credentials - withdrawal credentials used for generating the deposit data.
     * @param signature - BLS signature of the validator, generated by the operator.
     * @param deposit_data_root - hash tree root of the deposit data, generated by the operator.
     */
    struct ValidatorData {
        address operator;
        bytes pubkey;
        bytes32 withdrawal_credentials;
        bytes signature;
        bytes32 deposit_data_root; // more efficient to be calculated off-chain
    }

    /**
     * @dev Allows users to deposit funds into the contract.
     * @param data ValidatorData calldata containing the validator's public key, withdrawal credentials, and amount of tokens to be deposited.
     * @param latestDepositRoot bytes32 containing the latest deposit root.
     */
    function deposit(ValidatorData calldata data, bytes32 latestDepositRoot) external payable;

    function validators() external view returns (uint256);

    function mevEth() external view returns (address);

    /**
     * @notice VALIDATOR_DEPOSIT_SIZE()
     *
     * This function returns the size of the validator deposit.
     *
     * @dev This function is used to determine the size of the validator deposit. It is used to ensure that validators have the correct amount of funds in order
     * to participate in the network.
     */
    function VALIDATOR_DEPOSIT_SIZE() external view returns (uint256);

    // onlyAdmin Functions
    /**
     * @notice This function is used to pay rewards to the users.
     * @dev This function is used to pay rewards to the users. It takes in a uint256 rewards parameter which is the amount of rewards to be paid.
     */
    function payRewards(uint256 rewards) external;
    /**
     * @notice This function allows a validator to withdraw their rewards from the contract.
     * @dev This function is called by a validator to withdraw their rewards from the contract. It will transfer the rewards to the validator's address.
     */
    function payValidatorWithdraw() external;
    function recoverToken(address token, address recipient, uint256 amount) external;
    /**
     * @notice record() function is used to record the data in the smart contract.
     * @dev record() function takes no parameters and returns four uint128 values.
     */
    function record() external returns (uint128, uint128, uint128, uint128);
    /**
     * @notice registerExit() allows users to exit the system.
     * @dev registerExit() is a function that allows users to exit the system. It is triggered by an external call.
     */
    function registerExit() external;

    function batchMigrate(IStakingModule.ValidatorData[] calldata batchData) external;
}

File 10 of 11 : IERC20Burnable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IERC20Burnable {
    function burnFrom(address account, uint256 amount) external;
}

File 11 of 11 : ITinyMevEth.sol
/// SPDX-License-Identifier: SSPL-1.-0

/**
 * @custom:org.protocol='mevETH LST Protocol'
 * @custom:org.security='mailto:[email protected]'
 * @custom:org.vcs-commit=$GIT_COMMIT_SHA
 * @custom:org.vendor='CommodityStream, Inc'
 * @custom:org.schema-version="1.0"
 * @custom.org.encryption="manifoldfinance.com/.well-known/pgp-key.asc"
 * @custom:org.preferred-languages="en"
 */

pragma solidity ^0.8.19;

/// @title TinyMevEth
/// @notice smol interface for interacting with MevEth
interface ITinyMevEth {
    /**
     * @dev Function to grant rewards to other users.
     * @notice This function is payable and should be called with the amount of rewards to be granted.
     */
    function grantRewards() external payable;
    /**
     * @dev Function to allow a validator to withdraw funds from the contract.
     * @notice This function must be called with a validator address and a payable amount.
     */
    function grantValidatorWithdraw() external payable;
}

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "safe-contracts/=lib/safe-tools/lib/safe-contracts/contracts/",
    "safe-tools/=lib/safe-tools/src/",
    "properties/=lib/properties/contracts/",
    "solady/utils/=lib/solady/src/utils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 512,
    "details": {
      "constantOptimizer": true,
      "yul": true,
      "yulDetails": {
        "stackAllocation": true
      }
    }
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": false
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"authority","type":"address"},{"internalType":"address","name":"weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyClaimed","type":"error"},{"inputs":[],"name":"AlreadyDeposited","type":"error"},{"inputs":[],"name":"AlreadyFinalised","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"AlreadySet","type":"error"},{"inputs":[],"name":"DepositTooSmall","type":"error"},{"inputs":[],"name":"IncorrectWithdrawalCredentials","type":"error"},{"inputs":[],"name":"IndexExceedsQueueLength","type":"error"},{"inputs":[],"name":"InvalidPendingMevEthShareVault","type":"error"},{"inputs":[],"name":"InvalidPendingStakingModule","type":"error"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"NoAdmin","type":"error"},{"inputs":[],"name":"NotEnoughEth","type":"error"},{"inputs":[],"name":"NotFinalised","type":"error"},{"inputs":[],"name":"PrematureMevEthShareVaultUpdateFinalization","type":"error"},{"inputs":[],"name":"PrematureStakingModuleUpdateFinalization","type":"error"},{"inputs":[],"name":"SandwichProtection","type":"error"},{"inputs":[],"name":"StakingPaused","type":"error"},{"inputs":[],"name":"TransferExceedsAllowance","type":"error"},{"inputs":[],"name":"UnAuthorizedCaller","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"WithdrawTooSmall","type":"error"},{"inputs":[],"name":"WrongDepositAmount","type":"error"},{"inputs":[],"name":"WrongWithdrawAmount","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"}],"name":"AdminDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"creamAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mevEthAmount","type":"uint256"}],"name":"CreamRedeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"mevEthShareVault","type":"address"},{"indexed":true,"internalType":"address","name":"stakingModule","type":"address"}],"name":"MevEthInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldVault","type":"address"},{"indexed":true,"internalType":"address","name":"newVault","type":"address"}],"name":"MevEthShareVaultUpdateCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldVault","type":"address"},{"indexed":true,"internalType":"address","name":"pendingVault","type":"address"},{"indexed":true,"internalType":"uint64","name":"eligibleForFinalization","type":"uint64"}],"name":"MevEthShareVaultUpdateCommitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldVault","type":"address"},{"indexed":true,"internalType":"address","name":"newVault","type":"address"}],"name":"MevEthShareVaultUpdateFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"OperatorAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOperator","type":"address"}],"name":"OperatorDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Rewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldModule","type":"address"},{"indexed":true,"internalType":"address","name":"pendingModule","type":"address"}],"name":"StakingModuleUpdateCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldModule","type":"address"},{"indexed":true,"internalType":"address","name":"pendingModule","type":"address"},{"indexed":true,"internalType":"uint64","name":"eligibleForFinalization","type":"uint64"}],"name":"StakingModuleUpdateCommitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldModule","type":"address"},{"indexed":true,"internalType":"address","name":"newModule","type":"address"}],"name":"StakingModuleUpdateFinalized","type":"event"},{"anonymous":false,"inputs":[],"name":"StakingPaused","type":"event"},{"anonymous":false,"inputs":[],"name":"StakingUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"stakingModule","type":"address"},{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes32","name":"withdrawal_credentials","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"deposit_data_root","type":"bytes32"}],"indexed":false,"internalType":"struct IStakingModule.ValidatorData","name":"newValidator","type":"tuple"}],"name":"ValidatorCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ValidatorWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"withdrawalId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"WithdrawalQueueClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"withdrawalId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"WithdrawalQueueOpened","type":"event"},{"inputs":[],"name":"CREAM_TO_MEV_ETH_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_DEPOSIT","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_WITHDRAWAL","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"contract WETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAdmin","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"addOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"admins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"assetTokenAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calculateNeededEtherBuffer","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelUpdateMevEthShareVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelUpdateStakingModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"withdrawalId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMevEthShareVault","type":"address"}],"name":"commitUpdateMevEthShareVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStakingModule","name":"newModule","type":"address"}],"name":"commitUpdateStakingModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creamToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bytes","name":"pubkey","type":"bytes"},{"internalType":"bytes32","name":"withdrawal_credentials","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes32","name":"deposit_data_root","type":"bytes32"}],"internalType":"struct IStakingModule.ValidatorData","name":"newData","type":"tuple"},{"internalType":"bytes32","name":"latestDepositRoot","type":"bytes32"}],"name":"createValidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldAdmin","type":"address"}],"name":"deleteAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldOperator","type":"address"}],"name":"deleteOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"finalizeUpdateMevEthShareVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeUpdateStakingModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fraction","outputs":[{"internalType":"uint128","name":"elastic","type":"uint128"},{"internalType":"uint128","name":"base","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"grantRewards","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"grantValidatorWithdraw","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"initialShareVault","type":"address"},{"internalType":"address","name":"initialStakingModule","type":"address"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mevEthShareVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingMevEthShareVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingMevEthShareVaultCommittedTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingStakingModule","outputs":[{"internalType":"contract IStakingModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingStakingModuleCommittedTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newRequestsFinalisedUntil","type":"uint256"}],"name":"processWithdrawalQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"queueLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"creamAmount","type":"uint256"}],"name":"redeemCream","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"requestsFinalisedUntil","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"newMinimum","type":"uint128"}],"name":"setMinWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingModule","outputs":[{"internalType":"contract IStakingModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"totalManagedAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseStaking","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdrawQueue","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawalAmountQueued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ticketNumber","type":"uint256"}],"name":"withdrawalQueue","outputs":[{"internalType":"bool","name":"claimed","type":"bool"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint128","name":"amount","type":"uint128"},{"internalType":"uint128","name":"accumulatedAmount","type":"uint128"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

610100604081815234620004f157808262003af5803803809162000024828562000528565b833981010312620004f1576200003a826200054c565b906200004a60208094016200054c565b908051936200005985620004f6565b601a85527f4d6576204c6971756964205374616b696e672052656365697074000000000000818601528151916200009083620004f6565b60068352650dacaec8aa8960d31b828401526001600160a01b039485166000908152600283528181208054600160ff199182168117909255825460ff808216840116908216178355818552838320805482168317905588516001600160401b03999192918a8211620004dd57600391806200010c845462000561565b92601f938481116200048a575b508990848311600114620004275788926200041b575b505060001982851b1c191690841b1782555b8751908b8211620004075781906004996200015d8b5462000561565b828111620003b2575b50899183116001146200034f57879262000343575b505060001982841b1c191690831b1787555b60126080524660a052845180928590835493620001aa8562000561565b948585528a8086019884831692836000146200032357505050600114620002e3575b5050620001dc9250038262000528565b519020938251938401947f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8652838501527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608501524660808501523060a085015260a0845260c084019784891090891117620002d057505085905251902060c0521660e052600a80546001600160801b031916662386f26fc1000017905561355690816200059f823960805181610adc015260a05181612a46015260c05181612a6d015260e051818181610d1b01528181610dfe01528181611ec00152818161304e0152818161336d015261351e0152f35b634e487b7160e01b825260419052602490fd5b89925087528187209087915b8583106200030a575050620001dc93508201013880620001cc565b80548388018501528694508a93909201918101620002ef565b925092509350620001dc959250168652151560051b8201013880620001cc565b0151905038806200017b565b8a88528988208694509190601f198416895b8c8282106200039b575050841162000382575b505050811b0187556200018d565b015160001983861b60f8161c1916905538808062000374565b838501518655899790950194938401930162000361565b909192508a88528988208380860160051c8201928c8710620003fd575b91869589929594930160051c01915b828110620003ee57505062000166565b8a8155869550889101620003de565b92508192620003cf565b634e487b7160e01b86526041600452602486fd5b0151905038806200012f565b8589528a89208794509190601f1984168a5b8d8282106200047357505084116200045a575b505050811b01825562000141565b015160001983871b60f8161c191690553880806200044c565b8385015186558a9790950194938401930162000439565b9091508488528988208480850160051c8201928c8610620004d3575b918891869594930160051c01915b828110620004c457505062000119565b8a8155859450889101620004b4565b92508192620004a6565b634e487b7160e01b85526041600452602485fd5b600080fd5b604081019081106001600160401b038211176200051257604052565b634e487b7160e01b600052604160045260246000fd5b601f909101601f19168101906001600160401b038211908210176200051257604052565b51906001600160a01b0382168203620004f157565b90600182811c9216801562000593575b60208310146200057d57565b634e487b7160e01b600052602260045260246000fd5b91607f16916200057156fe60806040526004361015610023575b361561001957600080fd5b610021613514565b005b60003560e01c806301e1d1141461046f57806306fdde031461046a57806307a2d13a146103ac578063095ea7b3146104655780630a28a4771461046057806313e7c9d81461045b578063158ef93e1461045657806318160ddd1461045157806323b872dd1461044c57806327e1f7df146104475780632e92056d14610442578063313ce5671461043d578063342c00b3146104385780633644e51514610433578063379607f51461042e57806338d52e0f1461041f5780633cb5c58814610429578063402d267d1461037f578063429b62e5146104245780634aa4a4fc1461041f5780634cdad5061461041a578063504b82bf14610415578063558cb7f7146104105780636a4c66181461040b5780636ca6f0fe146104065780636e553f651461040157806370480275146103fc57806370a08231146103f757806372cf7751146103f25780637ecebe00146103ed57806382b9ebaa146103e85780638865cf50146103e35780638a1c2426146103de57806393f4bcde146103d957806394bf804d146103d457806395849aa4146103cf57806395d89b41146103ca5780639870d7fe146103c55780639ed89c91146103c0578063a9059cbb146103bb578063aa1cb376146103b6578063ab91c7b0146103b1578063b3d7f6b9146103ac578063b40992a1146103a7578063b460af94146103a2578063ba0876521461039d578063bbb781cc14610398578063bbbad84914610393578063bc74efe81461038e578063beb8db5614610389578063c1a7a81314610384578063c63d75b61461037f578063c6e6f5921461033e578063c822adda1461037a578063ce96cb7714610375578063d02aaa6514610370578063d15ca1661461036b578063d505accf14610366578063d8894bb514610361578063d905777e1461035c578063dd62ed3e14610357578063ddc2f1ab14610352578063df2d43d81461034d578063e1e158a514610348578063eb09200a14610343578063ef8b30f71461033e578063f09a401614610339578063f999c50614610334578063f9cc45f21461032f5763fe1832110361000e576128a3565b61287c565b612821565b612747565b612237565b612729565b612707565b6126d8565b61262a565b6125e0565b612573565b612544565b61234a565b612323565b6122fc565b6122b4565b612255565b610e4d565b6120ec565b611d5b565b611c20565b611b66565b611b43565b611af2565b611abd565b611a03565b61065f565b6119e5565b6118ed565b61182f565b611708565b611678565b6115d1565b6114d3565b61149b565b611443565b61125e565b61120c565b6111f1565b6111b3565b61118c565b61114e565b61108f565b611056565b611039565b61100e565b610f0a565b610ee3565b610eb6565b610dde565b610e73565b610e22565b610c2f565b610c14565b610b00565b610ac2565b610aa4565b610983565b6107dd565b6107bf565b610799565b610756565b610724565b610696565b61057a565b610484565b600091031261047f57565b600080fd5b3461047f57600036600319011261047f5760206001600160801b0360105416604051908152f35b90600182811c921680156104db575b60208310146104c557565b634e487b7160e01b600052602260045260246000fd5b91607f16916104ba565b67ffffffffffffffff81116104f957604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176104f957604052565b6020808252825181830181905290939260005b82811061056657505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610544565b3461047f5760008060031936011261065c57604051908060035461059d816104ab565b8085529160019180831690811561063257506001146105d7575b6105d3856105c78187038261050f565b60405191829182610531565b0390f35b9250600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b82841061061a5750505081016020016105c7826105d36105b7565b805460208587018101919091529093019281016105ff565b8695506105d3969350602092506105c794915060ff191682840152151560051b82010192936105b7565b80fd5b3461047f57602036600319011261047f57602061067d600435612f60565b604051908152f35b6001600160a01b0381160361047f57565b3461047f57604036600319011261047f576004356106b381610685565b6001600160a01b0360243591336000526007602052826106ea826040600020906001600160a01b0316600052602052604060002090565b5560405192835216907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461047f57602036600319011261047f57600435612710810481018091116107515761067d602091612f1e565b612c7b565b3461047f57602036600319011261047f576001600160a01b0360043561077b81610685565b166000526001602052602060ff604060002054166040519015158152f35b3461047f57600036600319011261047f57602060ff60095460081c166040519015158152f35b3461047f57600036600319011261047f576020600554604051908152f35b3461047f57606036600319011261047f576004356107fa81610685565b6024359061080782610685565b604435916001600160a01b03928383169261086160406000868152601160205281812054978616978882528282208054821161097b575b5050868152600760205220336001600160a01b0316600052602052604060002090565b54600181016108fd575b50906108d87fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef936108b26108ee946001600160a01b03166000526006602052604060002090565b6108bd848254612e49565b90556001600160a01b03166000526006602052604060002090565b8054820190556040519081529081906020820190565b0390a360405160018152602090f35b9190818303928311610751577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef936108b26108d8926108ee9561096f33610957846001600160a01b03166000526007602052604060002090565b906001600160a01b0316600052602052604060002090565b5593945050935061086b565b55388061083e565b3461047f57602036600319011261047f576004356109a081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0381169081835260026020526109e16109dd6040852060ff90541690565b1590565b610a8157610a0b6109fb6109f6855460ff1690565b613546565b60ff1660ff196000541617600055565b60ff610a18845460ff1690565b1615610a6f57610a3e610a48916001600160a01b03166000526002602052604060002090565b805460ff19169055565b7f989ddfce057dad219e0ae16f691b121bb0e348f0d8ae0ad400b4d5ac8d616c8b8280a280f35b604051631f8c1dbd60e11b8152600490fd5b60405163a741a04560e01b8152600490fd5b6040516282b42960e81b8152600490fd5b3461047f57600036600319011261047f576020601554604051908152f35b3461047f57600036600319011261047f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461047f57602036600319011261047f576004803533600052600160205260409160ff83600020541615610c07576013548211610bf9576015544780821015610be95790610b4d91612e49565b92601454808410610bdb57610b9e610baa91610b986001610b7f610b8782610b7f8b6000526016602052604060002090565b015460801c90565b936000526016602052604060002090565b90612edb565b6001600160801b031690565b809410610bce57610021610bc985610bc186601455565b601554612ca1565b601555565b5163f14a42b760e01b8152fd5b505163135bf97f60e11b8152fd5b845163f14a42b760e01b81528390fd5b82516312d29a5560e21b8152fd5b82516282b42960e81b8152fd5b3461047f57600036600319011261047f57602061067d612a41565b3461047f57602036600319011261047f576004356014548111610dcc57610c60816000526016602052604060002090565b805460ff16610dba57610c8d610c80836000526016602052604060002090565b805460ff19166001179055565b60018101610cb3610bc9610cab610b9e84546001600160801b031690565b601554612e49565b815460081c6001600160a01b0316610cd5610b9e83546001600160801b031690565b936040517f54a5cca61d01babb886db109822515b9fdff5360b38a042acfb13e47fab8b6fe6001600160a01b038094169180610d1689829190602083019252565b0390a37f00000000000000000000000000000000000000000000000000000000000000001691823b1561047f57600060049460405195868092630d0e30db60e41b8252875af1938415610db557610d88610d9692610b9e9261002197610d9c575b505460081c6001600160a01b031690565b92546001600160801b031690565b91612e56565b80610da9610daf926104e5565b80610474565b38610d77565b6129e9565b604051630c8d9eab60e31b8152600490fd5b6040516306e85c8160e21b8152600490fd5b3461047f57600036600319011261047f5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461047f57600036600319011261047f57602067ffffffffffffffff60095460101c16604051908152f35b3461047f57602036600319011261047f57610e69600435610685565b602061067d612fa2565b3461047f57602036600319011261047f576001600160a01b03600435610e9881610685565b166000526002602052602060ff604060002054166040519015158152f35b3461047f57602036600319011261047f57600435612710810481039081116107515761067d602091612f60565b3461047f57600036600319011261047f5760206001600160a01b03600d5416604051908152f35b600036600319011261047f57610f3a610f2e610f2e600d546001600160a01b031690565b6001600160a01b031690565b33148015610ff1575b15610fdf573415610fcd57610f91610f756001600160801b033416610f706010546001600160801b031690565b612e1f565b6001600160801b03166001600160801b03196010541617601055565b610f9a43600f55565b604080513381523460208201527fc083a1647e3ee591bf42b82564ffb4d16fdbb26068f0080da911c8d8300fd84a9190a1005b604051637c946ed760e01b8152600490fd5b60405163e3272bbb60e01b8152600490fd5b50611007610f2e600b546001600160a01b031690565b3314610f43565b3461047f57600036600319011261047f57602067ffffffffffffffff60095460501c16604051908152f35b3461047f57600036600319011261047f57602060405161046a8152f35b604036600319011261047f57602060043561067d60243561107681610685565b61107e612e01565b61108783612f1e565b928391612fbc565b3461047f57602036600319011261047f576004356110ac81610685565b600090338252600260205260ff60408320541615610a93576001600160a01b03811690818352600260205260ff604084205416610a815760ff83541660ff8114610751576111279161110e6001610c80930160ff1660ff196000541617600055565b6001600160a01b03166000526002602052604060002090565b7f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e3398280a280f35b3461047f57602036600319011261047f576001600160a01b0360043561117381610685565b1660005260066020526020604060002054604051908152f35b3461047f57600036600319011261047f5760206001600160a01b03600e5416604051908152f35b3461047f57602036600319011261047f576001600160a01b036004356111d881610685565b1660005260086020526020604060002054604051908152f35b3461047f57600036600319011261047f57602061067d612c0e565b3461047f57602036600319011261047f576004356001600160801b03811680910361047f5733600052600260205260ff6040600020541615610a93576001600160801b0319600a541617600a55600080f35b3461047f576003196040368201811361047f57600480359167ffffffffffffffff831161047f5760a083830194843603011261047f57600093338552600160205260ff828620541615611436576112b3612e01565b602484016112d46112cd6112c78385612cc9565b90612cfc565b5460ff1690565b61142757610c806112c76112e89284612cc9565b6112fd610f2e600d546001600160a01b031690565b926001600160a01b036044818616960135168503611419578251635552aa6560e01b8152936020858381845afa948515610db55787956113e9575b504761134b611345612c0e565b87612ca1565b116113da57908187923b156113d65761137c91855196878094819363b778a3a760e01b835288602435918401612dd4565b03925af1918215610db5577f8a8ef37c52979cf8197dd24ed66c48fbd26d1b35ee1879d8c0c6be67b64fe756936113bd936113c3575b505191829182612df0565b0390a280f35b80610da96113d0926104e5565b386113b2565b8280fd5b50825163f14a42b760e01b8152fd5b61140b91955060203d8111611412575b611403818361050f565b810190612bdf565b9338611338565b503d6113f9565b82516371b37a2760e11b8152fd5b50505163d5a8211560e01b8152fd5b50516282b42960e81b8152fd5b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760ff19600954166009557fa75958c26fdcd449db08b7c754dcddd7a15b023665ee9dbd2ef62d8e1befaa4a8180a180f35b604036600319011261047f57602060243561067d6004356114bb83610685565b6114c3612e01565b6114cc81612f60565b8093612fbc565b3461047f57602036600319011261047f576004356114f081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0380911680156115bf57600e805473ffffffffffffffffffffffffffffffffffffffff1916821790556009805469ffffffffffffffff000019164260101b69ffffffffffffffff00001617905567ffffffffffffffff9161157b610f2e600d546001600160a01b031690565b9261159561158842612c91565b67ffffffffffffffff1690565b1692167f39610571f23fd1a159473075de5b697023e7a31da8488147ecce3c05489885fa8480a480f35b60405163817ae11560e01b8152600490fd5b3461047f5760008060031936011261065c5760405190806004546115f4816104ab565b80855291600191808316908115610632575060011461161d576105d3856105c78187038261050f565b9250600483527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106116605750505081016020016105c7826105d36105b7565b80546020858701810191909152909301928101611645565b3461047f57602036600319011261047f5760043561169581610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0316808252600160205260ff604083205416610a8157808252600160205260408220600160ff198254161790557fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d8280a280f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760095460101c67ffffffffffffffff16600e546001600160a01b03166001600160a01b0391828216908115801561181d575b6115bf5761158861177191612cae565b67ffffffffffffffff42161061180b576117db9261179a610f2e600d546001600160a01b031690565b167f2d2c1ec12191e7f1a0c23a865475c7abecc7f26edb6c409defa31748b28a50138580a36001600160a01b03166001600160a01b0319600d541617600d55565b6117f06001600160a01b0319600e5416600e55565b61180869ffffffffffffffff00001960095416600955565b80f35b6040516310733cc760e31b8152600490fd5b5067ffffffffffffffff811615611761565b3461047f5760408060031936011261047f5760043561184d81610685565b602435600033815260116020526001600160a01b0384822054931692838252848220805482116118e5575b50503381526006602052838120908154838103908111610751578592558381526006602052208181540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8351806118d83394829190602083019252565b0390a35160018152602090f35b553880611878565b3461047f57602036600319011261047f5760043561190a81610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0380911680156119d357600c805473ffffffffffffffffffffffffffffffffffffffff1916821790556009805471ffffffffffffffff0000000000000000000019164260501b71ffffffffffffffff000000000000000000001617905567ffffffffffffffff91600b546001600160a01b0316926119a961158842612c91565b1692167fa8aa7c0b023219361c11e0a52a6ae2e6b7404aa61f5df0fc03d6cb8acafd66bf8480a480f35b60405163d92e233d60e01b8152600490fd5b3461047f57600036600319011261047f576020601354604051908152f35b3461047f57602036600319011261047f57600435611a2081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0316808252600160205260ff60408320541615610a8157808252600160205260408220805460ff191690557f69df2c5ec2ea4d1fbe1e503524f593b356162ca710671263827f2e1992b95ae18280a280f35b606090600319011261047f5760043590602435611aad81610685565b90604435611aba81610685565b90565b3461047f57611acb36611a91565b90612710830483019081841161075157602093611aea61067d93612f1e565b93849261326d565b3461047f57611b0036611a91565b909161271081046005548083810311610751578214611b3b575b81039080821161075157602093611b3361067d93612f60565b93849161326d565b506000611b1a565b3461047f57600036600319011261047f57602060ff600954166040519015158152f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a93576001600160a01b0380611bab610f2e600e546001600160a01b031690565b169081158015611c09575b6115bf57611bcf610f2e600d546001600160a01b031690565b167f372791c0ff91275afe8e3b839b282b0dbb7bb639dbe7cd5896eedfec4c8ed8c68380a36117f06001600160a01b0319600e5416600e55565b5060095460101c67ffffffffffffffff1615611bb6565b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760095460501c67ffffffffffffffff16600c546001600160a01b03166001600160a01b03918282169081158015611d49575b611d3757611588611c8991612cae565b67ffffffffffffffff421610611d2557611cf092611caf600b546001600160a01b031690565b167f4dfa26cce610e1567cfad6de12824202e3394a8e66665914b3aeec46b60eca118580a36001600160a01b03166001600160a01b0319600b541617600b55565b611d056001600160a01b0319600c5416600c55565b61180871ffffffffffffffff000000000000000000001960095416600955565b604051631a5340df60e31b8152600490fd5b604051634326d9bf60e11b8152600490fd5b5067ffffffffffffffff811615611c79565b3461047f57611d6936611a91565b919061271082046001600160801b0390816010541680858103116107515784146120e4575b830180841161075157611da090612f1e565b92611db6610b9e600a546001600160801b031690565b81106120d25733600090815260116020526040902094611dd96000965443612e49565b1580156120a4575b80612091575b61207f57611df58582613475565b611e15610f75848416611e106010546001600160801b031690565b612edb565b611e45611e2a848716611e1060105460801c90565b6001600160801b036010549181199060801b16911617601055565b611e4f8582613417565b611e5c4760155490612e49565b928293838110611f37575b505082611e7a575b602085604051908152f35b60408051928352602083018690526001600160a01b03928584169284169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9190a47f00000000000000000000000000000000000000000000000000000000000000001691823b15611f3357604051630d0e30db60e41b8152948560048185875af1928315610db557602095611f1794611f20575b50612e56565b38808080611e6f565b80610da9611f2d926104e5565b38611f11565b8480fd5b91935061203c611f478385612e49565b91611f5b611f5660135461323e565b601355565b8216611fca601354611f7b611f8b84610f706001610b7f611f7b87612e3a565b6000526016602052604060002090565b611fba611f9661324d565b600081526001600160a01b038d166020820152956001600160801b03166040870152565b6001600160801b03166060850152565b906001908051151560ff845491168060ff19831617855574ffffffffffffffffffffffffffffffffffffffff00602084015160081b16916affffffffffffffffffffff60a81b16171783556001600160801b036040820151169060606001600160801b031991015160801b1617910155565b601354604051918252906001600160a01b038616907f09dfd37369506d687db24de2b5f681eb0050ccd07eaafa95d0252899471dd74090602090a3913880611e67565b604051637ef2d89b60e01b8152600490fd5b5061209e600f5443612e49565b15611de7565b506120cc6120c5826001600160a01b03166000526011602052604060002090565b5443612e49565b15611de1565b6040516393c76c6f60e01b8152600490fd5b506000611d8e565b3461047f57602036600319011261047f57600435612108612e01565b8015610fcd5761212261211a82612ef4565b6103e8900490565b662386f26fc10000811061222557612171611e2a61213f83612f1e565b92612163610f756001600160801b03809316610f706010546001600160801b031690565b8316610f7060105460801c90565b7349d72e3973900a195a155a46441f0c08179fdb6490813b1561047f5760405163079cc67960e41b815233600482015260248101849052916000908390604490829084905af1908115610db5577f08a1751668afae790ff5a3e76783eb5dc7c53adc0b248d4af119bf0edb29f97c9261220d92612212575b506121f48133613159565b6040805194855260208501919091523393918291820190565b0390a2005b80610da961221f926104e5565b386121e9565b604051636ba4a1c760e01b8152600490fd5b3461047f57602036600319011261047f57602061067d600435612f1e565b3461047f57602036600319011261047f57600435600052601660205260806040600020600181549101546001600160a01b036040519260ff81161515845260081c1660208301526001600160801b0381166040830152821c6060820152f35b3461047f57602036600319011261047f57602061067d6004356122d681610685565b6001600160a01b03479116600052600683526122f6604060002054612f60565b90613507565b3461047f57600036600319011261047f5760206001600160a01b03600c5416604051908152f35b3461047f57600036600319011261047f5760206001600160801b03600a5416604051908152f35b3461047f5760e036600319011261047f5760043561236781610685565b6024359061237482610685565b604435606435926084359360ff8516850361047f576124ac60209161239b4282101561299d565b61247361247f6123a9612a41565b92886123c8816001600160a01b03166000526008602052604060002090565b8054906001820190556124366040519384928c8c8c8601968791959493909260a09360c08401977f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c985526001600160a01b038092166020860152166040840152606083015260808201520152565b039161244a601f199384810183528261050f565b5190206040519384918883019687909160429261190160f01b8352600283015260228201520190565b0390810183528261050f565b5190206040805191825260ff909716602082015260a4359681019690965260c43560608701526080860190565b856000968792838052039060015afa15610db5577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259084519083612525826109576001600160a01b039561250c8782168015159081612538575b506129f5565b6001600160a01b03166000526007602052604060002090565b556040519384528116931691602090a380f35b9050888c161438612506565b3461047f57600036600319011261047f5760406010548151906001600160801b038116825260801c6020820152f35b3461047f57602036600319011261047f57602061067d60043561259581610685565b6001600160a01b036125a647612f1e565b91166000526006835260406000205490613507565b604090600319011261047f576004356125d381610685565b90602435611aba81610685565b3461047f5760206126216001600160a01b036125fb366125bb565b9116600052600783526040600020906001600160a01b0316600052602052604060002090565b54604051908152f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a93576001600160a01b038061266c600c546001600160a01b031690565b1690811580156126c1575b611d3757600b546001600160a01b0316167f84251438ec5abdb3e7f88a1f74e49cc798585f96428d53f9b3b85232b7f4bde38380a3611d056001600160a01b0319600c5416600c55565b5060095460501c67ffffffffffffffff1615612677565b3461047f57600036600319011261047f5760206040517349d72e3973900a195a155a46441f0c08179fdb648152f35b3461047f57600036600319011261047f576020604051662386f26fc100008152f35b3461047f57600036600319011261047f576020601454604051908152f35b3461047f57612755366125bb565b90600091338352600260205260ff60408420541615610a93576001600160a01b038083169182156119d357169182156119d35760095460081c60ff16612810576127c9906127ad61010061ff00196009541617600955565b6001600160a01b03166001600160a01b0319600b541617600b55565b6127e9826001600160a01b03166001600160a01b0319600d541617600d55565b7f64ba02ab01156808d0b519bed16cc382b3fc7fbe9bae6951b30fb08742b824a88380a380f35b60405162dc149f60e41b8152600490fd5b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a9357600160ff1960095416176009557f26d1807b479eaba249c1214b82e4b65bbb0cc73ee8a17901324b1ef1b5904e498180a180f35b3461047f57600036600319011261047f5760206001600160a01b03600b5416604051908152f35b60008060031936011261065c576128c5610f2e600d546001600160a01b031690565b6001600160a01b03811633148015612980575b1561296e576801bc16d674ec800000340361295c57604080513381523460208201528392917f12b964a3993d1598dd8a3b627a3b90b4bc6b7a8f4f8bb6afde02a30d178e28ef91a1803b15612959578190600460405180948193634ad8d34b60e01b83525af18015610db55761294c575080f35b80610da9611808926104e5565b50fd5b6040516328b8c64b60e11b8152600490fd5b604051636edaef2f60e11b8152600490fd5b50612996610f2e600b546001600160a01b031690565b33146128d8565b156129a457565b60405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606490fd5b6040513d6000823e3d90fd5b156129fc57565b60405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606490fd5b6000467f000000000000000000000000000000000000000000000000000000000000000003612a8f57507f000000000000000000000000000000000000000000000000000000000000000090565b604051600354919081612aa1846104ab565b80835260209485840194600191878382169182600014612bba575050600114612b61575b5050509181612adc612b5b93612b4d95038261050f565b519020604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f95810195865260208601929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69085015246606085015230608085015291829060a0850190565b03601f19810183528261050f565b51902090565b9190869350600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b828410612ba55750505082010181612adc612b5b612ac5565b80548685018601528794909301928101612b8c565b60ff1916885293151560051b86019093019350849250612adc9150612b5b9050612ac5565b9081602091031261047f575190565b8115612bf8570490565b634e487b7160e01b600052601260045260246000fd5b601554600460206001600160a01b03600d541660405192838092635552aa6560e01b82525afa908115610db557605a91606491600091612c5d575b50040280821115612c58575090565b905090565b612c75915060203d811161141257611403818361050f565b38612c49565b634e487b7160e01b600052601160045260246000fd5b9062093a80820180921161075157565b9190820180921161075157565b9062093a8067ffffffffffffffff8093160191821161075157565b903590601e198136030182121561047f570180359067ffffffffffffffff821161047f5760200191813603831361047f57565b6020908260405193849283378101601281520301902090565b9035601e198236030181121561047f57016020813591019167ffffffffffffffff821161047f57813603831361047f57565b908060209392818452848401376000828201840152601f01601f1916010190565b906001600160a01b038235612d7c81610685565b168152608080612dcb612da6612d956020870187612d15565b60a0602088015260a0870191612d47565b60408601356040860152612dbd6060870187612d15565b908683036060880152612d47565b93013591015290565b929190612deb602091604086526040860190612d68565b930152565b906020611aba928181520190612d68565b60ff60095416612e0d57565b6040516326d1807b60e01b8152600490fd5b9190916001600160801b038080941691160191821161075157565b60001981019190821161075157565b9190820391821161075157565b9091602090604460405194600080958194829363a9059cbb60e01b84526004526024525af13d15601f3d116001845114161716906060528160405215612e995750565b62461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606490fd5b6001600160801b03918216908216039190821161075157565b9061046a9182810292818404149015171561075157565b8181029291811591840414171561075157565b6010546001600160801b0381169081158015612f55575b15612f3f57505090565b611aba92612f509160801c90612f0b565b612bee565b508060801c15612f35565b6010546001600160801b03811680158015612f97575b15612f8057505090565b612f8d90611aba93612f0b565b9060801c90612bee565b508160801c15612f76565b60ff60095416612fb7576001600160801b0390565b600090565b662386f26fc10000821061222557613001611e2a6001600160801b03612ff3610f75828716610f706010546001600160801b031690565b8516610f7060105460801c90565b4361301f336001600160a01b03166000526011602052604060002090565b554361303e826001600160a01b03166000526011602052604060002090565b5534613111576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169061307b833033856131b4565b813b1561047f57604051632e1a7d4d60e01b815260048101849052916000908390602490829084905af1908115610db5577fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7926001600160a01b03926130fe575b505b6130e88582613159565b60408051948552602085019590955216923392a3565b80610da961310b926104e5565b386130dc565b813403613147576001600160a01b037fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7916130de565b604051636ff0acf960e11b8152600490fd5b6005548281018091116107515760206001600160a01b036000937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9360055516938484526006825260408420818154019055604051908152a3565b9192606460209294604051956000958694859384936323b872dd60e01b85526004526024526044525af13d15601f3d1160018451141617169060605281604052156131fc5750565b62461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606490fd5b60001981146107515760010190565b604051906080820182811067ffffffffffffffff8211176104f957604052565b9290613284610b9e600a546001600160801b031690565b83106120d2576132aa6120c5336001600160a01b03166000526011602052604060002090565b1580156133f0575b806133dd575b61207f576132c68282613475565b6132fd611e2a6001600160801b036132ef610f75828816611e106010546001600160801b031690565b8416611e1060105460801c90565b6133078282613417565b826133154760155490612e49565b106133cb5782613326575b50505050565b6040805184815260208101939093526001600160a01b03928584169284169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9190a47f00000000000000000000000000000000000000000000000000000000000000001691823b1561047f57604051630d0e30db60e41b81529260008460048186855af1938415610db5576133c294611f205750612e56565b38808080613320565b60405163f14a42b760e01b8152600490fd5b506133ea600f5443612e49565b156132b8565b506134116120c5826001600160a01b03166000526011602052604060002090565b156132b2565b6001600160a01b03168060005260066020526040600020805490838203918211610751576000937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092558060055403600555604051908152a3565b6001600160a01b03168060005260076020526134a76040600020336001600160a01b0316600052602052604060002090565b543382036134b457505050565b8281106134f557196134c4575050565b60005260076020526134ed336040600020906001600160a01b0316600052602052604060002090565b908154039055565b604051630e81252160e01b8152600490fd5b9080821015612c58575090565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361296e57565b60ff1680156107515760001901905600000000000000000000000018f3e9ab3dcd396c2d3e6e598a9f77621ea50fc3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x60806040526004361015610023575b361561001957600080fd5b610021613514565b005b60003560e01c806301e1d1141461046f57806306fdde031461046a57806307a2d13a146103ac578063095ea7b3146104655780630a28a4771461046057806313e7c9d81461045b578063158ef93e1461045657806318160ddd1461045157806323b872dd1461044c57806327e1f7df146104475780632e92056d14610442578063313ce5671461043d578063342c00b3146104385780633644e51514610433578063379607f51461042e57806338d52e0f1461041f5780633cb5c58814610429578063402d267d1461037f578063429b62e5146104245780634aa4a4fc1461041f5780634cdad5061461041a578063504b82bf14610415578063558cb7f7146104105780636a4c66181461040b5780636ca6f0fe146104065780636e553f651461040157806370480275146103fc57806370a08231146103f757806372cf7751146103f25780637ecebe00146103ed57806382b9ebaa146103e85780638865cf50146103e35780638a1c2426146103de57806393f4bcde146103d957806394bf804d146103d457806395849aa4146103cf57806395d89b41146103ca5780639870d7fe146103c55780639ed89c91146103c0578063a9059cbb146103bb578063aa1cb376146103b6578063ab91c7b0146103b1578063b3d7f6b9146103ac578063b40992a1146103a7578063b460af94146103a2578063ba0876521461039d578063bbb781cc14610398578063bbbad84914610393578063bc74efe81461038e578063beb8db5614610389578063c1a7a81314610384578063c63d75b61461037f578063c6e6f5921461033e578063c822adda1461037a578063ce96cb7714610375578063d02aaa6514610370578063d15ca1661461036b578063d505accf14610366578063d8894bb514610361578063d905777e1461035c578063dd62ed3e14610357578063ddc2f1ab14610352578063df2d43d81461034d578063e1e158a514610348578063eb09200a14610343578063ef8b30f71461033e578063f09a401614610339578063f999c50614610334578063f9cc45f21461032f5763fe1832110361000e576128a3565b61287c565b612821565b612747565b612237565b612729565b612707565b6126d8565b61262a565b6125e0565b612573565b612544565b61234a565b612323565b6122fc565b6122b4565b612255565b610e4d565b6120ec565b611d5b565b611c20565b611b66565b611b43565b611af2565b611abd565b611a03565b61065f565b6119e5565b6118ed565b61182f565b611708565b611678565b6115d1565b6114d3565b61149b565b611443565b61125e565b61120c565b6111f1565b6111b3565b61118c565b61114e565b61108f565b611056565b611039565b61100e565b610f0a565b610ee3565b610eb6565b610dde565b610e73565b610e22565b610c2f565b610c14565b610b00565b610ac2565b610aa4565b610983565b6107dd565b6107bf565b610799565b610756565b610724565b610696565b61057a565b610484565b600091031261047f57565b600080fd5b3461047f57600036600319011261047f5760206001600160801b0360105416604051908152f35b90600182811c921680156104db575b60208310146104c557565b634e487b7160e01b600052602260045260246000fd5b91607f16916104ba565b67ffffffffffffffff81116104f957604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff8211176104f957604052565b6020808252825181830181905290939260005b82811061056657505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610544565b3461047f5760008060031936011261065c57604051908060035461059d816104ab565b8085529160019180831690811561063257506001146105d7575b6105d3856105c78187038261050f565b60405191829182610531565b0390f35b9250600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b82841061061a5750505081016020016105c7826105d36105b7565b805460208587018101919091529093019281016105ff565b8695506105d3969350602092506105c794915060ff191682840152151560051b82010192936105b7565b80fd5b3461047f57602036600319011261047f57602061067d600435612f60565b604051908152f35b6001600160a01b0381160361047f57565b3461047f57604036600319011261047f576004356106b381610685565b6001600160a01b0360243591336000526007602052826106ea826040600020906001600160a01b0316600052602052604060002090565b5560405192835216907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461047f57602036600319011261047f57600435612710810481018091116107515761067d602091612f1e565b612c7b565b3461047f57602036600319011261047f576001600160a01b0360043561077b81610685565b166000526001602052602060ff604060002054166040519015158152f35b3461047f57600036600319011261047f57602060ff60095460081c166040519015158152f35b3461047f57600036600319011261047f576020600554604051908152f35b3461047f57606036600319011261047f576004356107fa81610685565b6024359061080782610685565b604435916001600160a01b03928383169261086160406000868152601160205281812054978616978882528282208054821161097b575b5050868152600760205220336001600160a01b0316600052602052604060002090565b54600181016108fd575b50906108d87fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef936108b26108ee946001600160a01b03166000526006602052604060002090565b6108bd848254612e49565b90556001600160a01b03166000526006602052604060002090565b8054820190556040519081529081906020820190565b0390a360405160018152602090f35b9190818303928311610751577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef936108b26108d8926108ee9561096f33610957846001600160a01b03166000526007602052604060002090565b906001600160a01b0316600052602052604060002090565b5593945050935061086b565b55388061083e565b3461047f57602036600319011261047f576004356109a081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0381169081835260026020526109e16109dd6040852060ff90541690565b1590565b610a8157610a0b6109fb6109f6855460ff1690565b613546565b60ff1660ff196000541617600055565b60ff610a18845460ff1690565b1615610a6f57610a3e610a48916001600160a01b03166000526002602052604060002090565b805460ff19169055565b7f989ddfce057dad219e0ae16f691b121bb0e348f0d8ae0ad400b4d5ac8d616c8b8280a280f35b604051631f8c1dbd60e11b8152600490fd5b60405163a741a04560e01b8152600490fd5b6040516282b42960e81b8152600490fd5b3461047f57600036600319011261047f576020601554604051908152f35b3461047f57600036600319011261047f57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b3461047f57602036600319011261047f576004803533600052600160205260409160ff83600020541615610c07576013548211610bf9576015544780821015610be95790610b4d91612e49565b92601454808410610bdb57610b9e610baa91610b986001610b7f610b8782610b7f8b6000526016602052604060002090565b015460801c90565b936000526016602052604060002090565b90612edb565b6001600160801b031690565b809410610bce57610021610bc985610bc186601455565b601554612ca1565b601555565b5163f14a42b760e01b8152fd5b505163135bf97f60e11b8152fd5b845163f14a42b760e01b81528390fd5b82516312d29a5560e21b8152fd5b82516282b42960e81b8152fd5b3461047f57600036600319011261047f57602061067d612a41565b3461047f57602036600319011261047f576004356014548111610dcc57610c60816000526016602052604060002090565b805460ff16610dba57610c8d610c80836000526016602052604060002090565b805460ff19166001179055565b60018101610cb3610bc9610cab610b9e84546001600160801b031690565b601554612e49565b815460081c6001600160a01b0316610cd5610b9e83546001600160801b031690565b936040517f54a5cca61d01babb886db109822515b9fdff5360b38a042acfb13e47fab8b6fe6001600160a01b038094169180610d1689829190602083019252565b0390a37f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691823b1561047f57600060049460405195868092630d0e30db60e41b8252875af1938415610db557610d88610d9692610b9e9261002197610d9c575b505460081c6001600160a01b031690565b92546001600160801b031690565b91612e56565b80610da9610daf926104e5565b80610474565b38610d77565b6129e9565b604051630c8d9eab60e31b8152600490fd5b6040516306e85c8160e21b8152600490fd5b3461047f57600036600319011261047f5760206040516001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168152f35b3461047f57600036600319011261047f57602067ffffffffffffffff60095460101c16604051908152f35b3461047f57602036600319011261047f57610e69600435610685565b602061067d612fa2565b3461047f57602036600319011261047f576001600160a01b03600435610e9881610685565b166000526002602052602060ff604060002054166040519015158152f35b3461047f57602036600319011261047f57600435612710810481039081116107515761067d602091612f60565b3461047f57600036600319011261047f5760206001600160a01b03600d5416604051908152f35b600036600319011261047f57610f3a610f2e610f2e600d546001600160a01b031690565b6001600160a01b031690565b33148015610ff1575b15610fdf573415610fcd57610f91610f756001600160801b033416610f706010546001600160801b031690565b612e1f565b6001600160801b03166001600160801b03196010541617601055565b610f9a43600f55565b604080513381523460208201527fc083a1647e3ee591bf42b82564ffb4d16fdbb26068f0080da911c8d8300fd84a9190a1005b604051637c946ed760e01b8152600490fd5b60405163e3272bbb60e01b8152600490fd5b50611007610f2e600b546001600160a01b031690565b3314610f43565b3461047f57600036600319011261047f57602067ffffffffffffffff60095460501c16604051908152f35b3461047f57600036600319011261047f57602060405161046a8152f35b604036600319011261047f57602060043561067d60243561107681610685565b61107e612e01565b61108783612f1e565b928391612fbc565b3461047f57602036600319011261047f576004356110ac81610685565b600090338252600260205260ff60408320541615610a93576001600160a01b03811690818352600260205260ff604084205416610a815760ff83541660ff8114610751576111279161110e6001610c80930160ff1660ff196000541617600055565b6001600160a01b03166000526002602052604060002090565b7f44d6d25963f097ad14f29f06854a01f575648a1ef82f30e562ccd3889717e3398280a280f35b3461047f57602036600319011261047f576001600160a01b0360043561117381610685565b1660005260066020526020604060002054604051908152f35b3461047f57600036600319011261047f5760206001600160a01b03600e5416604051908152f35b3461047f57602036600319011261047f576001600160a01b036004356111d881610685565b1660005260086020526020604060002054604051908152f35b3461047f57600036600319011261047f57602061067d612c0e565b3461047f57602036600319011261047f576004356001600160801b03811680910361047f5733600052600260205260ff6040600020541615610a93576001600160801b0319600a541617600a55600080f35b3461047f576003196040368201811361047f57600480359167ffffffffffffffff831161047f5760a083830194843603011261047f57600093338552600160205260ff828620541615611436576112b3612e01565b602484016112d46112cd6112c78385612cc9565b90612cfc565b5460ff1690565b61142757610c806112c76112e89284612cc9565b6112fd610f2e600d546001600160a01b031690565b926001600160a01b036044818616960135168503611419578251635552aa6560e01b8152936020858381845afa948515610db55787956113e9575b504761134b611345612c0e565b87612ca1565b116113da57908187923b156113d65761137c91855196878094819363b778a3a760e01b835288602435918401612dd4565b03925af1918215610db5577f8a8ef37c52979cf8197dd24ed66c48fbd26d1b35ee1879d8c0c6be67b64fe756936113bd936113c3575b505191829182612df0565b0390a280f35b80610da96113d0926104e5565b386113b2565b8280fd5b50825163f14a42b760e01b8152fd5b61140b91955060203d8111611412575b611403818361050f565b810190612bdf565b9338611338565b503d6113f9565b82516371b37a2760e11b8152fd5b50505163d5a8211560e01b8152fd5b50516282b42960e81b8152fd5b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760ff19600954166009557fa75958c26fdcd449db08b7c754dcddd7a15b023665ee9dbd2ef62d8e1befaa4a8180a180f35b604036600319011261047f57602060243561067d6004356114bb83610685565b6114c3612e01565b6114cc81612f60565b8093612fbc565b3461047f57602036600319011261047f576004356114f081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0380911680156115bf57600e805473ffffffffffffffffffffffffffffffffffffffff1916821790556009805469ffffffffffffffff000019164260101b69ffffffffffffffff00001617905567ffffffffffffffff9161157b610f2e600d546001600160a01b031690565b9261159561158842612c91565b67ffffffffffffffff1690565b1692167f39610571f23fd1a159473075de5b697023e7a31da8488147ecce3c05489885fa8480a480f35b60405163817ae11560e01b8152600490fd5b3461047f5760008060031936011261065c5760405190806004546115f4816104ab565b80855291600191808316908115610632575060011461161d576105d3856105c78187038261050f565b9250600483527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106116605750505081016020016105c7826105d36105b7565b80546020858701810191909152909301928101611645565b3461047f57602036600319011261047f5760043561169581610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0316808252600160205260ff604083205416610a8157808252600160205260408220600160ff198254161790557fac6fa858e9350a46cec16539926e0fde25b7629f84b5a72bffaae4df888ae86d8280a280f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760095460101c67ffffffffffffffff16600e546001600160a01b03166001600160a01b0391828216908115801561181d575b6115bf5761158861177191612cae565b67ffffffffffffffff42161061180b576117db9261179a610f2e600d546001600160a01b031690565b167f2d2c1ec12191e7f1a0c23a865475c7abecc7f26edb6c409defa31748b28a50138580a36001600160a01b03166001600160a01b0319600d541617600d55565b6117f06001600160a01b0319600e5416600e55565b61180869ffffffffffffffff00001960095416600955565b80f35b6040516310733cc760e31b8152600490fd5b5067ffffffffffffffff811615611761565b3461047f5760408060031936011261047f5760043561184d81610685565b602435600033815260116020526001600160a01b0384822054931692838252848220805482116118e5575b50503381526006602052838120908154838103908111610751578592558381526006602052208181540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8351806118d83394829190602083019252565b0390a35160018152602090f35b553880611878565b3461047f57602036600319011261047f5760043561190a81610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0380911680156119d357600c805473ffffffffffffffffffffffffffffffffffffffff1916821790556009805471ffffffffffffffff0000000000000000000019164260501b71ffffffffffffffff000000000000000000001617905567ffffffffffffffff91600b546001600160a01b0316926119a961158842612c91565b1692167fa8aa7c0b023219361c11e0a52a6ae2e6b7404aa61f5df0fc03d6cb8acafd66bf8480a480f35b60405163d92e233d60e01b8152600490fd5b3461047f57600036600319011261047f576020601354604051908152f35b3461047f57602036600319011261047f57600435611a2081610685565b600090338252600260205260ff60408320541615610a93576001600160a01b0316808252600160205260ff60408320541615610a8157808252600160205260408220805460ff191690557f69df2c5ec2ea4d1fbe1e503524f593b356162ca710671263827f2e1992b95ae18280a280f35b606090600319011261047f5760043590602435611aad81610685565b90604435611aba81610685565b90565b3461047f57611acb36611a91565b90612710830483019081841161075157602093611aea61067d93612f1e565b93849261326d565b3461047f57611b0036611a91565b909161271081046005548083810311610751578214611b3b575b81039080821161075157602093611b3361067d93612f60565b93849161326d565b506000611b1a565b3461047f57600036600319011261047f57602060ff600954166040519015158152f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a93576001600160a01b0380611bab610f2e600e546001600160a01b031690565b169081158015611c09575b6115bf57611bcf610f2e600d546001600160a01b031690565b167f372791c0ff91275afe8e3b839b282b0dbb7bb639dbe7cd5896eedfec4c8ed8c68380a36117f06001600160a01b0319600e5416600e55565b5060095460101c67ffffffffffffffff1615611bb6565b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a935760095460501c67ffffffffffffffff16600c546001600160a01b03166001600160a01b03918282169081158015611d49575b611d3757611588611c8991612cae565b67ffffffffffffffff421610611d2557611cf092611caf600b546001600160a01b031690565b167f4dfa26cce610e1567cfad6de12824202e3394a8e66665914b3aeec46b60eca118580a36001600160a01b03166001600160a01b0319600b541617600b55565b611d056001600160a01b0319600c5416600c55565b61180871ffffffffffffffff000000000000000000001960095416600955565b604051631a5340df60e31b8152600490fd5b604051634326d9bf60e11b8152600490fd5b5067ffffffffffffffff811615611c79565b3461047f57611d6936611a91565b919061271082046001600160801b0390816010541680858103116107515784146120e4575b830180841161075157611da090612f1e565b92611db6610b9e600a546001600160801b031690565b81106120d25733600090815260116020526040902094611dd96000965443612e49565b1580156120a4575b80612091575b61207f57611df58582613475565b611e15610f75848416611e106010546001600160801b031690565b612edb565b611e45611e2a848716611e1060105460801c90565b6001600160801b036010549181199060801b16911617601055565b611e4f8582613417565b611e5c4760155490612e49565b928293838110611f37575b505082611e7a575b602085604051908152f35b60408051928352602083018690526001600160a01b03928584169284169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9190a47f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691823b15611f3357604051630d0e30db60e41b8152948560048185875af1928315610db557602095611f1794611f20575b50612e56565b38808080611e6f565b80610da9611f2d926104e5565b38611f11565b8480fd5b91935061203c611f478385612e49565b91611f5b611f5660135461323e565b601355565b8216611fca601354611f7b611f8b84610f706001610b7f611f7b87612e3a565b6000526016602052604060002090565b611fba611f9661324d565b600081526001600160a01b038d166020820152956001600160801b03166040870152565b6001600160801b03166060850152565b906001908051151560ff845491168060ff19831617855574ffffffffffffffffffffffffffffffffffffffff00602084015160081b16916affffffffffffffffffffff60a81b16171783556001600160801b036040820151169060606001600160801b031991015160801b1617910155565b601354604051918252906001600160a01b038616907f09dfd37369506d687db24de2b5f681eb0050ccd07eaafa95d0252899471dd74090602090a3913880611e67565b604051637ef2d89b60e01b8152600490fd5b5061209e600f5443612e49565b15611de7565b506120cc6120c5826001600160a01b03166000526011602052604060002090565b5443612e49565b15611de1565b6040516393c76c6f60e01b8152600490fd5b506000611d8e565b3461047f57602036600319011261047f57600435612108612e01565b8015610fcd5761212261211a82612ef4565b6103e8900490565b662386f26fc10000811061222557612171611e2a61213f83612f1e565b92612163610f756001600160801b03809316610f706010546001600160801b031690565b8316610f7060105460801c90565b7349d72e3973900a195a155a46441f0c08179fdb6490813b1561047f5760405163079cc67960e41b815233600482015260248101849052916000908390604490829084905af1908115610db5577f08a1751668afae790ff5a3e76783eb5dc7c53adc0b248d4af119bf0edb29f97c9261220d92612212575b506121f48133613159565b6040805194855260208501919091523393918291820190565b0390a2005b80610da961221f926104e5565b386121e9565b604051636ba4a1c760e01b8152600490fd5b3461047f57602036600319011261047f57602061067d600435612f1e565b3461047f57602036600319011261047f57600435600052601660205260806040600020600181549101546001600160a01b036040519260ff81161515845260081c1660208301526001600160801b0381166040830152821c6060820152f35b3461047f57602036600319011261047f57602061067d6004356122d681610685565b6001600160a01b03479116600052600683526122f6604060002054612f60565b90613507565b3461047f57600036600319011261047f5760206001600160a01b03600c5416604051908152f35b3461047f57600036600319011261047f5760206001600160801b03600a5416604051908152f35b3461047f5760e036600319011261047f5760043561236781610685565b6024359061237482610685565b604435606435926084359360ff8516850361047f576124ac60209161239b4282101561299d565b61247361247f6123a9612a41565b92886123c8816001600160a01b03166000526008602052604060002090565b8054906001820190556124366040519384928c8c8c8601968791959493909260a09360c08401977f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c985526001600160a01b038092166020860152166040840152606083015260808201520152565b039161244a601f199384810183528261050f565b5190206040519384918883019687909160429261190160f01b8352600283015260228201520190565b0390810183528261050f565b5190206040805191825260ff909716602082015260a4359681019690965260c43560608701526080860190565b856000968792838052039060015afa15610db5577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259084519083612525826109576001600160a01b039561250c8782168015159081612538575b506129f5565b6001600160a01b03166000526007602052604060002090565b556040519384528116931691602090a380f35b9050888c161438612506565b3461047f57600036600319011261047f5760406010548151906001600160801b038116825260801c6020820152f35b3461047f57602036600319011261047f57602061067d60043561259581610685565b6001600160a01b036125a647612f1e565b91166000526006835260406000205490613507565b604090600319011261047f576004356125d381610685565b90602435611aba81610685565b3461047f5760206126216001600160a01b036125fb366125bb565b9116600052600783526040600020906001600160a01b0316600052602052604060002090565b54604051908152f35b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a93576001600160a01b038061266c600c546001600160a01b031690565b1690811580156126c1575b611d3757600b546001600160a01b0316167f84251438ec5abdb3e7f88a1f74e49cc798585f96428d53f9b3b85232b7f4bde38380a3611d056001600160a01b0319600c5416600c55565b5060095460501c67ffffffffffffffff1615612677565b3461047f57600036600319011261047f5760206040517349d72e3973900a195a155a46441f0c08179fdb648152f35b3461047f57600036600319011261047f576020604051662386f26fc100008152f35b3461047f57600036600319011261047f576020601454604051908152f35b3461047f57612755366125bb565b90600091338352600260205260ff60408420541615610a93576001600160a01b038083169182156119d357169182156119d35760095460081c60ff16612810576127c9906127ad61010061ff00196009541617600955565b6001600160a01b03166001600160a01b0319600b541617600b55565b6127e9826001600160a01b03166001600160a01b0319600d541617600d55565b7f64ba02ab01156808d0b519bed16cc382b3fc7fbe9bae6951b30fb08742b824a88380a380f35b60405162dc149f60e41b8152600490fd5b3461047f5760008060031936011261065c57338152600260205260ff60408220541615610a9357600160ff1960095416176009557f26d1807b479eaba249c1214b82e4b65bbb0cc73ee8a17901324b1ef1b5904e498180a180f35b3461047f57600036600319011261047f5760206001600160a01b03600b5416604051908152f35b60008060031936011261065c576128c5610f2e600d546001600160a01b031690565b6001600160a01b03811633148015612980575b1561296e576801bc16d674ec800000340361295c57604080513381523460208201528392917f12b964a3993d1598dd8a3b627a3b90b4bc6b7a8f4f8bb6afde02a30d178e28ef91a1803b15612959578190600460405180948193634ad8d34b60e01b83525af18015610db55761294c575080f35b80610da9611808926104e5565b50fd5b6040516328b8c64b60e11b8152600490fd5b604051636edaef2f60e11b8152600490fd5b50612996610f2e600b546001600160a01b031690565b33146128d8565b156129a457565b60405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606490fd5b6040513d6000823e3d90fd5b156129fc57565b60405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e45520000000000000000000000000000000000006044820152606490fd5b6000467f000000000000000000000000000000000000000000000000000000000000000103612a8f57507fb0a8412dd8498ed14bdf74a71dbfd6a88b0e37f1a55ff39d82a1a2dd6194eb7690565b604051600354919081612aa1846104ab565b80835260209485840194600191878382169182600014612bba575050600114612b61575b5050509181612adc612b5b93612b4d95038261050f565b519020604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f95810195865260208601929092527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc69085015246606085015230608085015291829060a0850190565b03601f19810183528261050f565b51902090565b9190869350600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b828410612ba55750505082010181612adc612b5b612ac5565b80548685018601528794909301928101612b8c565b60ff1916885293151560051b86019093019350849250612adc9150612b5b9050612ac5565b9081602091031261047f575190565b8115612bf8570490565b634e487b7160e01b600052601260045260246000fd5b601554600460206001600160a01b03600d541660405192838092635552aa6560e01b82525afa908115610db557605a91606491600091612c5d575b50040280821115612c58575090565b905090565b612c75915060203d811161141257611403818361050f565b38612c49565b634e487b7160e01b600052601160045260246000fd5b9062093a80820180921161075157565b9190820180921161075157565b9062093a8067ffffffffffffffff8093160191821161075157565b903590601e198136030182121561047f570180359067ffffffffffffffff821161047f5760200191813603831361047f57565b6020908260405193849283378101601281520301902090565b9035601e198236030181121561047f57016020813591019167ffffffffffffffff821161047f57813603831361047f57565b908060209392818452848401376000828201840152601f01601f1916010190565b906001600160a01b038235612d7c81610685565b168152608080612dcb612da6612d956020870187612d15565b60a0602088015260a0870191612d47565b60408601356040860152612dbd6060870187612d15565b908683036060880152612d47565b93013591015290565b929190612deb602091604086526040860190612d68565b930152565b906020611aba928181520190612d68565b60ff60095416612e0d57565b6040516326d1807b60e01b8152600490fd5b9190916001600160801b038080941691160191821161075157565b60001981019190821161075157565b9190820391821161075157565b9091602090604460405194600080958194829363a9059cbb60e01b84526004526024525af13d15601f3d116001845114161716906060528160405215612e995750565b62461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606490fd5b6001600160801b03918216908216039190821161075157565b9061046a9182810292818404149015171561075157565b8181029291811591840414171561075157565b6010546001600160801b0381169081158015612f55575b15612f3f57505090565b611aba92612f509160801c90612f0b565b612bee565b508060801c15612f35565b6010546001600160801b03811680158015612f97575b15612f8057505090565b612f8d90611aba93612f0b565b9060801c90612bee565b508160801c15612f76565b60ff60095416612fb7576001600160801b0390565b600090565b662386f26fc10000821061222557613001611e2a6001600160801b03612ff3610f75828716610f706010546001600160801b031690565b8516610f7060105460801c90565b4361301f336001600160a01b03166000526011602052604060002090565b554361303e826001600160a01b03166000526011602052604060002090565b5534613111576001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169061307b833033856131b4565b813b1561047f57604051632e1a7d4d60e01b815260048101849052916000908390602490829084905af1908115610db5577fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7926001600160a01b03926130fe575b505b6130e88582613159565b60408051948552602085019590955216923392a3565b80610da961310b926104e5565b386130dc565b813403613147576001600160a01b037fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7916130de565b604051636ff0acf960e11b8152600490fd5b6005548281018091116107515760206001600160a01b036000937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9360055516938484526006825260408420818154019055604051908152a3565b9192606460209294604051956000958694859384936323b872dd60e01b85526004526024526044525af13d15601f3d1160018451141617169060605281604052156131fc5750565b62461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606490fd5b60001981146107515760010190565b604051906080820182811067ffffffffffffffff8211176104f957604052565b9290613284610b9e600a546001600160801b031690565b83106120d2576132aa6120c5336001600160a01b03166000526011602052604060002090565b1580156133f0575b806133dd575b61207f576132c68282613475565b6132fd611e2a6001600160801b036132ef610f75828816611e106010546001600160801b031690565b8416611e1060105460801c90565b6133078282613417565b826133154760155490612e49565b106133cb5782613326575b50505050565b6040805184815260208101939093526001600160a01b03928584169284169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db9190a47f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21691823b1561047f57604051630d0e30db60e41b81529260008460048186855af1938415610db5576133c294611f205750612e56565b38808080613320565b60405163f14a42b760e01b8152600490fd5b506133ea600f5443612e49565b156132b8565b506134116120c5826001600160a01b03166000526011602052604060002090565b156132b2565b6001600160a01b03168060005260066020526040600020805490838203918211610751576000937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092558060055403600555604051908152a3565b6001600160a01b03168060005260076020526134a76040600020336001600160a01b0316600052602052604060002090565b543382036134b457505050565b8281106134f557196134c4575050565b60005260076020526134ed336040600020906001600160a01b0316600052602052604060002090565b908154039055565b604051630e81252160e01b8152600490fd5b9080821015612c58575090565b6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216330361296e57565b60ff16801561075157600019019056

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

00000000000000000000000018f3e9ab3dcd396c2d3e6e598a9f77621ea50fc3000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : authority (address): 0x18F3e9Ab3dcd396C2d3e6e598a9F77621Ea50fC3
Arg [1] : weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000018f3e9ab3dcd396c2d3e6e598a9f77621ea50fc3
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.