Feature Tip: Add private address tag to any address under My Name Tag !
ERC-20
Staking
Overview
Max Total Supply
1,802.237590711614505501 mevETH
Holders
338 (0.00%)
Market
Price
$2,693.17 @ 1.066419 ETH
Onchain Market Cap
$4,853,732.21
Circulating Supply Market Cap
$4,853,731.00
Other Info
Token Contract (WITH 18 Decimals)
Balance
0 mevETHValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|---|---|---|---|---|
1 | ![]() | 0X5E8422345238F34275888049021821E8E08CAA1F-0X24AE2DA0F361AA4BE46B48EB19C91E02C5E4F27E | $2,607.07 1.0319290 Eth | $716.21 0.286 0X5E8422345238F34275888049021821E8E08CAA1F | 100.0000% |
2 | ![]() | 0X24AE2DA0F361AA4BE46B48EB19C91E02C5E4F27E-0XD084944D3C05CD115C09D072B9F44BA3E0E45921 | $3,501.60 1.3331230 Eth | $56.73 0.016 0X24AE2DA0F361AA4BE46B48EB19C91E02C5E4F27E | 5.6737% |
Contract Name:
MevEth
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 512 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/// 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); } }
/// 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); } }
// 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"); } }
// 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)) } } }
// 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); } }
/// 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); }
// 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(); } }
/// 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(); }
/// 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; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface IERC20Burnable { function burnFrom(address account, uint256 amount) external; }
/// 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; }
{ "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
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
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.