Linea Testnet

Contract

0xD2725ab62B3A747576F244b824d76E06b38D63A9

Overview

ETH Balance

Linea Sepolia LogoLinea Sepolia LogoLinea Sepolia Logo0 ETH

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
120921292025-04-20 2:23:5112 mins ago1745115831
0xD2725ab6...6b38D63A9
0 ETH
120921292025-04-20 2:23:5112 mins ago1745115831
0xD2725ab6...6b38D63A9
0 ETH
120921152025-04-20 2:23:2313 mins ago1745115803
0xD2725ab6...6b38D63A9
0 ETH
120921152025-04-20 2:23:2313 mins ago1745115803
0xD2725ab6...6b38D63A9
0 ETH
120919652025-04-20 2:18:2318 mins ago1745115503
0xD2725ab6...6b38D63A9
0 ETH
120919652025-04-20 2:18:2318 mins ago1745115503
0xD2725ab6...6b38D63A9
0 ETH
120919002025-04-20 2:16:1320 mins ago1745115373
0xD2725ab6...6b38D63A9
0 ETH
120919002025-04-20 2:16:1320 mins ago1745115373
0xD2725ab6...6b38D63A9
0 ETH
120918912025-04-20 2:15:5520 mins ago1745115355
0xD2725ab6...6b38D63A9
0 ETH
120918792025-04-20 2:15:2521 mins ago1745115325
0xD2725ab6...6b38D63A9
0 ETH
120918792025-04-20 2:15:2521 mins ago1745115325
0xD2725ab6...6b38D63A9
0 ETH
120918712025-04-20 2:14:5321 mins ago1745115293
0xD2725ab6...6b38D63A9
0 ETH
120918712025-04-20 2:14:5321 mins ago1745115293
0xD2725ab6...6b38D63A9
0 ETH
120918472025-04-20 2:14:0522 mins ago1745115245
0xD2725ab6...6b38D63A9
0 ETH
120917862025-04-20 2:12:0324 mins ago1745115123
0xD2725ab6...6b38D63A9
0 ETH
120917502025-04-20 2:10:5125 mins ago1745115051
0xD2725ab6...6b38D63A9
0 ETH
120916012025-04-20 2:05:5330 mins ago1745114753
0xD2725ab6...6b38D63A9
0 ETH
120915642025-04-20 2:04:3931 mins ago1745114679
0xD2725ab6...6b38D63A9
0 ETH
120915542025-04-20 2:04:1932 mins ago1745114659
0xD2725ab6...6b38D63A9
0 ETH
120915542025-04-20 2:04:1932 mins ago1745114659
0xD2725ab6...6b38D63A9
0 ETH
120915542025-04-20 2:04:1932 mins ago1745114659
0xD2725ab6...6b38D63A9
0 ETH
120915542025-04-20 2:04:1932 mins ago1745114659
0xD2725ab6...6b38D63A9
0 ETH
120915432025-04-20 2:03:5732 mins ago1745114637
0xD2725ab6...6b38D63A9
0 ETH
120915432025-04-20 2:03:5732 mins ago1745114637
0xD2725ab6...6b38D63A9
0 ETH
120915152025-04-20 2:03:0133 mins ago1745114581
0xD2725ab6...6b38D63A9
0 ETH
View All Internal Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xD4Da4DB4...bB7d14eA6
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
mErc20Host

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 2499 runs

Other Settings:
london EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 25 : mErc20Host.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {Steel} from "risc0/steel/Steel.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// contracts
import {ZkVerifier} from "src/verifier/ZkVerifier.sol";
import {mErc20Upgradable} from "src/mToken/mErc20Upgradable.sol";

import {mTokenProofDecoderLib} from "src/libraries/mTokenProofDecoderLib.sol";

import {ImErc20Host} from "src/interfaces/ImErc20Host.sol";
import {ImTokenOperationTypes} from "src/interfaces/ImToken.sol";
import {IRoles} from "src/interfaces/IRoles.sol";

contract mErc20Host is mErc20Upgradable, ZkVerifier, ImErc20Host, ImTokenOperationTypes {
    using SafeERC20 for IERC20;

    // ----------- STORAGE ------------
    mapping(uint32 => mapping(address => uint256)) public accAmountInPerChain;
    mapping(uint32 => mapping(address => uint256)) public accAmountOutPerChain;
    mapping(address => mapping(address => bool)) public allowedCallers;
    mapping(uint32 => bool) public allowedChains;

    /**
     * @notice Initializes the new money market
     * @param underlying_ The address of the underlying asset
     * @param operator_ The address of the Operator
     * @param interestRateModel_ The address of the interest rate model
     * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
     * @param name_ ERC-20 name of this token
     * @param symbol_ ERC-20 symbol of this token
     * @param decimals_ ERC-20 decimal precision of this token
     * @param admin_ Address of the administrator of this token
     * @param zkVerifier_ The IRiscZeroVerifier address
     */
    function initialize(
        address underlying_,
        address operator_,
        address interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address payable admin_,
        address zkVerifier_,
        address roles_
    ) external initializer {
        // Initialize the base contract
        proxyInitialize(
            underlying_, operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_, admin_
        );

        // Initialize the ZkVerifier
        ZkVerifier.initialize(zkVerifier_);

        rolesOperator = IRoles(roles_);

        // Set the proper admin now that initialization is done
        admin = admin_;
    }

    // ----------- VIEW ------------
    /**
     * @inheritdoc ImErc20Host
     */
    function isCallerAllowed(address sender, address caller) external view returns (bool) {
        return allowedCallers[sender][caller];
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function getProofData(address user, uint32 dstId) external view returns (bytes memory) {
        return mTokenProofDecoderLib.encodeJournal(
            user,
            address(this),
            accAmountInPerChain[dstId][user],
            accAmountOutPerChain[dstId][user],
            uint32(block.chainid),
            dstId
        );
    }

    // ----------- OWNER ------------
    /**
     * @notice Sets the _risc0Verifier address
     * @param _risc0Verifier the new IRiscZeroVerifier address
     */
    function setVerifier(address _risc0Verifier) external onlyAdmin {
        _setVerifier(_risc0Verifier);
    }

    /**
     * @notice Sets the image id
     * @param _imageId the new image id
     */
    function setImageId(bytes32 _imageId) external onlyAdmin {
        _setImageId(_imageId);
    }

    function setTotalUnderlying(uint256 _amount) external onlyAdmin {
        initialExchangeRateMantissa = _amount;
    }

    /**
     * @notice Updates an allowed chain status
     * @param _chainId the chain id
     * @param _status the new status
     */
    function updateAllowedChain(uint32 _chainId, bool _status) external {
        if (msg.sender != admin && !rolesOperator.isAllowedFor(msg.sender, rolesOperator.CHAINS_MANAGER())) {
            revert mErc20Host_CallerNotAllowed();
        }
        allowedChains[_chainId] = _status;
        emit mErc20Host_ChainStatusUpdated(_chainId, _status);
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function extractForRebalancing(uint256 amount) external {
        if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.REBALANCER())) revert mErc20Host_NotRebalancer();
        IERC20(underlying).safeTransfer(msg.sender, amount);
    }

    // ----------- PUBLIC ------------
    /**
     * @inheritdoc ImErc20Host
     */
    function updateAllowedCallerStatus(address caller, bool status) external override {
        allowedCallers[msg.sender][caller] = status;
        emit AllowedCallerUpdated(msg.sender, caller, status);
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function liquidateExternal(
        bytes calldata journalData,
        bytes calldata seal,
        address[] calldata userToLiquidate,
        uint256[] calldata liquidateAmount,
        address[] calldata collateral,
        address receiver
    ) external override {
        // verify received data
        if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
            _verifyProof(journalData, seal);
        }

        bytes[] memory journals = abi.decode(journalData, (bytes[]));
        uint256 length = journals.length;
        require(length == liquidateAmount.length, mErc20Host_LengthMismatch());
        require(length == userToLiquidate.length, mErc20Host_LengthMismatch());
        require(length == collateral.length, mErc20Host_LengthMismatch());

        for (uint256 i; i < length;) {
            _liquidateExternal(journals[i], userToLiquidate[i], liquidateAmount[i], collateral[i], receiver);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function mintExternal(
        bytes calldata journalData,
        bytes calldata seal,
        uint256[] calldata mintAmount,
        address receiver
    ) external override {
        if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
            _verifyProof(journalData, seal);
        }

        bytes[] memory journals = abi.decode(journalData, (bytes[]));
        uint256 length = journals.length;
        require(length == mintAmount.length, mErc20Host_LengthMismatch());

        for (uint256 i; i < length;) {
            _mintExternal(journals[i], mintAmount[i], receiver);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function repayExternal(
        bytes calldata journalData,
        bytes calldata seal,
        uint256[] calldata repayAmount,
        address receiver
    ) external override {
        if (!rolesOperator.isAllowedFor(msg.sender, rolesOperator.PROOF_BATCH_FORWARDER())) {
            _verifyProof(journalData, seal);
        }

        bytes[] memory journals = abi.decode(journalData, (bytes[]));
        uint256 length = journals.length;
        require(length == repayAmount.length, mErc20Host_LengthMismatch());

        for (uint256 i; i < length;) {
            _repayExternal(journals[i], repayAmount[i], receiver);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function withdrawOnExtension(uint256 amount, uint32 dstChainId) external override {
        require(amount > 0, mErc20Host_AmountNotValid());

        // actions
        accAmountOutPerChain[dstChainId][msg.sender] += amount;
        _redeemUnderlying(msg.sender, amount, false);

        emit mErc20Host_WithdrawOnExtensionChain(msg.sender, dstChainId, amount);
    }

    /**
     * @inheritdoc ImErc20Host
     */
    function borrowOnExtension(uint256 amount, uint32 dstChainId) external override {
        require(amount > 0, mErc20Host_AmountNotValid());

        // actions
        accAmountOutPerChain[dstChainId][msg.sender] += amount;
        _borrow(msg.sender, amount, false);

        emit mErc20Host_BorrowOnExternsionChain(msg.sender, dstChainId, amount);
    }

    // ----------- PRIVATE ------------
    function _verifyProof(bytes calldata journalData, bytes calldata seal) private {
        require(journalData.length > 0, mErc20Host_JournalNotValid());

        // verify it using the ZkVerifier contract
        _verifyInput(journalData, seal);
    }

    function _checkSender(address msgSender, address srcSender) private view {
        if (msgSender != srcSender) {
            require(
                allowedCallers[srcSender][msgSender] || msgSender == admin
                    || rolesOperator.isAllowedFor(msgSender, rolesOperator.PROOF_FORWARDER())
                    || rolesOperator.isAllowedFor(msgSender, rolesOperator.PROOF_BATCH_FORWARDER()),
                mErc20Host_CallerNotAllowed()
            );
        }
    }

    function _liquidateExternal(
        bytes memory singleJournal,
        address userToLiquidate,
        uint256 liquidateAmount,
        address collateral,
        address receiver
    ) internal {
        (address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
            mTokenProofDecoderLib.decodeJournal(singleJournal);

        receiver = _sender;

        // base checks
        {
            _checkSender(msg.sender, _sender);
            require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
            require(_market == address(this), mErc20Host_AddressNotValid());
            require(allowedChains[_chainId], mErc20Host_ChainNotValid());
        }
        // operation checks
        {
            require(liquidateAmount > 0, mErc20Host_AmountNotValid());
            require(liquidateAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
            require(userToLiquidate != msg.sender && userToLiquidate != _sender, mErc20Host_CallerNotAllowed());
        }
        collateral = collateral == address(0) ? address(this) : collateral;

        // actions
        accAmountInPerChain[_chainId][_sender] += liquidateAmount;
        _liquidate(receiver, userToLiquidate, liquidateAmount, collateral, false);

        emit mErc20Host_LiquidateExternal(
            msg.sender, _sender, userToLiquidate, receiver, collateral, _chainId, liquidateAmount
        );
    }

    function _mintExternal(bytes memory singleJournal, uint256 mintAmount, address receiver) internal {
        (address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
            mTokenProofDecoderLib.decodeJournal(singleJournal);

        receiver = _sender;

        // base checks
        {
            _checkSender(msg.sender, _sender);
            require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
            require(_market == address(this), mErc20Host_AddressNotValid());
            require(allowedChains[_chainId], mErc20Host_ChainNotValid());
        }
        // operation checks
        {
            require(mintAmount > 0, mErc20Host_AmountNotValid());
            require(mintAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
        }

        // actions
        accAmountInPerChain[_chainId][_sender] += mintAmount;
        _mint(receiver, mintAmount, false);

        emit mErc20Host_MintExternal(msg.sender, _sender, receiver, _chainId, mintAmount);
    }

    function _repayExternal(bytes memory singleJournal, uint256 repayAmount, address receiver) internal {
        (address _sender, address _market, uint256 _accAmountIn,, uint32 _chainId, uint32 _dstChainId) =
            mTokenProofDecoderLib.decodeJournal(singleJournal);

        receiver = _sender;

        // base checks
        {
            _checkSender(msg.sender, _sender);
            require(_dstChainId == uint32(block.chainid), mErc20Host_DstChainNotValid());
            require(_market == address(this), mErc20Host_AddressNotValid());
            require(allowedChains[_chainId], mErc20Host_ChainNotValid());
        }
        // operation checks
        {
            require(repayAmount > 0, mErc20Host_AmountNotValid());
            require(repayAmount <= _accAmountIn - accAmountInPerChain[_chainId][_sender], mErc20Host_AmountTooBig());
        }

        // actions
        accAmountInPerChain[_chainId][_sender] += repayAmount;
        _repayBehalf(receiver, repayAmount, false);

        emit mErc20Host_RepayExternal(msg.sender, _sender, receiver, _chainId, repayAmount);
    }
}

File 2 of 25 : Steel.sol
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.9;

/// @title Steel Library
/// @notice This library provides a collection of utilities to work with Steel commitments in Solidity.
library Steel {
    /// @notice Represents a commitment to a specific block in the blockchain.
    /// @dev The `id` combines the version and the actual identifier of the claim, such as the block number.
    /// @dev The `digest` represents the data being committed to, e.g. the hash of the execution block.
    /// @dev The `configID` is the cryptographic digest of the network configuration.
    struct Commitment {
        uint256 id;
        bytes32 digest;
        bytes32 configID;
    }

    /// @notice The version of the Commitment is incorrect.
    error InvalidCommitmentVersion();

    /// @notice The Commitment is too old and can no longer be validated.
    error CommitmentTooOld();

    /// @notice Validates if the provided Commitment matches the block hash of the given block number.
    /// @param commitment The Commitment struct to validate.
    /// @return True if the commitment's block hash matches the block hash of the block number, false otherwise.
    function validateCommitment(Commitment memory commitment) internal view returns (bool) {
        (uint240 claimID, uint16 version) = Encoding.decodeVersionedID(commitment.id);
        if (version == 0) {
            return validateBlockCommitment(claimID, commitment.digest);
        } else if (version == 1) {
            return validateBeaconCommitment(claimID, commitment.digest);
        } else {
            revert InvalidCommitmentVersion();
        }
    }

    /// @notice Validates if the provided block commitment matches the block hash of the given block number.
    /// @param blockNumber The block number to compare against.
    /// @param blockHash The block hash to validate.
    /// @return True if the block's block hash matches the block hash, false otherwise.
    function validateBlockCommitment(uint256 blockNumber, bytes32 blockHash) internal view returns (bool) {
        if (block.number - blockNumber > 256) {
            revert CommitmentTooOld();
        }
        return blockHash == blockhash(blockNumber);
    }

    /// @notice Validates if the provided beacon commitment matches the block root of the given timestamp.
    /// @param timestamp The timestamp to compare against.
    /// @param blockRoot The block root to validate.
    /// @return True if the block's block root matches the block root, false otherwise.
    function validateBeaconCommitment(uint256 timestamp, bytes32 blockRoot) internal view returns (bool) {
        if (block.timestamp - timestamp > 12 * 8191) {
            revert CommitmentTooOld();
        }
        return blockRoot == Beacon.parentBlockRoot(timestamp);
    }
}

/// @title Beacon Library
library Beacon {
    /// @notice The address of the Beacon roots contract.
    /// @dev https://eips.ethereum.org/EIPS/eip-4788
    address internal constant BEACON_ROOTS_ADDRESS = 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02;

    /// @notice Find the root of the Beacon block corresponding to the parent of the execution block with the given timestamp.
    /// @return root Returns the corresponding Beacon block root or null, if no such block exists.
    function parentBlockRoot(uint256 timestamp) internal view returns (bytes32 root) {
        (bool success, bytes memory result) = BEACON_ROOTS_ADDRESS.staticcall(abi.encode(timestamp));
        if (success) {
            return abi.decode(result, (bytes32));
        }
    }
}

/// @title Encoding Library
library Encoding {
    /// @notice Encodes a version and ID into a single uint256 value.
    /// @param id The base ID to be encoded, limited by 240 bits (or the maximum value of a uint240).
    /// @param version The version number to be encoded, limited by 16 bits (or the maximum value of a uint16).
    /// @return Returns a single uint256 value that contains both the `id` and the `version` encoded into it.
    function encodeVersionedID(uint240 id, uint16 version) internal pure returns (uint256) {
        uint256 encoded;
        assembly {
            encoded := or(shl(240, version), id)
        }
        return encoded;
    }

    /// @notice Decodes a version and ID from a single uint256 value.
    /// @param id The single uint256 value to be decoded.
    /// @return Returns two values: a uint240 for the original base ID and a uint16 for the version number encoded into it.
    function decodeVersionedID(uint256 id) internal pure returns (uint240, uint16) {
        uint240 decoded;
        uint16 version;
        assembly {
            decoded := and(id, 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            version := shr(240, id)
        }
        return (decoded, version);
    }
}

File 3 of 25 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 4 of 25 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

File 5 of 25 : ZkVerifier.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {IRiscZeroVerifier} from "risc0/IRiscZeroVerifier.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

// contracts
import {Steel} from "risc0/steel/Steel.sol";

abstract contract ZkVerifier is Initializable {
    // ----------- STORAGE ------------
    IRiscZeroVerifier public verifier;

    bytes32 public imageId;

    error ZkVerifier_ImageNotValid();
    error ZkVerifier_InputNotValid();
    error ZkVerifier_VerifierNotSet();

    event ImageSet(bytes32 _imageId);
    event VerifierSet(address indexed oldVerifier, address indexed newVerifier);

    // ----------- PUBLIC ------------
    /**
     * @notice Initializes a new ZkVerifier contract
     * @param _verifier IRiscZeroVerifier contract implementation
     */
    function initialize(address _verifier) public initializer {
        require(_verifier != address(0), ZkVerifier_InputNotValid());
        verifier = IRiscZeroVerifier(_verifier);
    }

    // ----------- INTERNAL ------------
    /**
     * @notice Sets the _risc0Verifier address
     * @dev Admin check is needed on the external method
     * @param _risc0Verifier the new IRiscZeroVerifier address
     */
    function _setVerifier(address _risc0Verifier) internal {
        require(_risc0Verifier != address(0), ZkVerifier_InputNotValid());
        emit VerifierSet(address(verifier), _risc0Verifier);
        verifier = IRiscZeroVerifier(_risc0Verifier);
    }

    /**
     * @notice Sets the image id
     * @dev Admin check is needed on the external method
     * @param _imageId the new image id
     */
    function _setImageId(bytes32 _imageId) internal {
        require(_imageId != bytes32(0), ZkVerifier_ImageNotValid());
        emit ImageSet(_imageId);
        imageId = _imageId;
    }

    /**
     * @notice Verifies an input
     * @param journalEntry the risc0 journal entry
     * @param seal the risc0 seal
     */
    function _verifyInput(bytes calldata journalEntry, bytes calldata seal) internal virtual {
        // generic checks
        _checkAddresses();

        // verify input
        __verify(journalEntry, seal);
    }

    // ----------- PRIVATE ------------
    function _checkAddresses() private view {
        require(address(verifier) != address(0), ZkVerifier_VerifierNotSet());
    }

    function __verify(bytes calldata journalEntry, bytes calldata seal) private view {
        verifier.verify(seal, imageId, sha256(journalEntry));
    }
}

File 6 of 25 : mErc20Upgradable.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

import {mErc20} from "./mErc20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract mErc20Upgradable is mErc20, Initializable {
    /**
     * @notice Initialize the new money market
     * @param underlying_ The address of the underlying asset
     * @param operator_ The address of the Operator
     * @param interestRateModel_ The address of the interest rate model
     * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
     * @param name_ ERC-20 name of this token
     * @param symbol_ ERC-20 symbol of this token
     * @param decimals_ ERC-20 decimal precision of this token
     */
    function proxyInitialize(
        address underlying_,
        address operator_,
        address interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        address payable admin_
    ) public {
        // Creator of the contract is admin during initialization
        admin = payable(msg.sender);

        // mErc20 initialize does the bulk of the work
        super.initialize(
            underlying_, operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_
        );

        // Set the proper admin now that initialization is done
        admin = admin_;
    }
}

File 7 of 25 : mTokenProofDecoderLib.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

import {BytesLib} from "src/libraries/BytesLib.sol";

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

library mTokenProofDecoderLib {
    uint256 public constant ENTRY_SIZE = 112; // single journal entry size

    error mTokenProofDecoderLib_ChainNotFound();

    function decodeJournal(bytes memory journalData)
        internal
        pure
        returns (
            address sender,
            address market,
            uint256 accAmountIn,
            uint256 accAmountOut,
            uint32 chainId,
            uint32 dstChainId
        )
    {
        // decode action data
        // | Offset | Length | Data Type               |
        // |--------|---------|----------------------- |
        // | 0      | 20      | address sender         |
        // | 20     | 40      | address market         |
        // | 40     | 32      | uint256 accAmountIn    |
        // | 72     | 32      | uint256 accAmountOut   |
        // | 104    | 4       | uint32 chainId         |
        sender = BytesLib.toAddress(BytesLib.slice(journalData, 0, 20), 0);
        market = BytesLib.toAddress(BytesLib.slice(journalData, 20, 20), 0);
        accAmountIn = BytesLib.toUint256(BytesLib.slice(journalData, 40, 32), 0);
        accAmountOut = BytesLib.toUint256(BytesLib.slice(journalData, 72, 32), 0);
        chainId = BytesLib.toUint32(BytesLib.slice(journalData, 104, 4), 0);
        dstChainId = BytesLib.toUint32(BytesLib.slice(journalData, 108, 4), 0);
    }

    function encodeJournal(
        address sender,
        address market,
        uint256 accAmountIn,
        uint256 accAmountOut,
        uint32 chainId,
        uint32 dstChainId
    ) internal pure returns (bytes memory) {
        return abi.encodePacked(sender, market, accAmountIn, accAmountOut, chainId, dstChainId);
    }
}

File 8 of 25 : ImErc20Host.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

interface ImErc20Host {
    // ----------- EVENTS -----------
    /**
     * @notice Emitted when a user updates allowed callers
     */
    event AllowedCallerUpdated(address indexed sender, address indexed caller, bool status);

    /**
     * @notice Emitted when a chain id whitelist status is updated
     */
    event mErc20Host_ChainStatusUpdated(uint32 indexed chainId, bool status);

    /**
     * @notice Emitted when a liquidate operation is executed
     */
    event mErc20Host_LiquidateExternal(
        address indexed msgSender,
        address indexed srcSender,
        address userToLiquidate,
        address receiver,
        address indexed collateral,
        uint32 srcChainId,
        uint256 amount
    );

    /**
     * @notice Emitted when a mint operation is executed
     */
    event mErc20Host_MintExternal(
        address indexed msgSender, address indexed srcSender, address indexed receiver, uint32 chainId, uint256 amount
    );

    /**
     * @notice Emitted when a borrow operation is executed
     */
    event mErc20Host_BorrowExternal(
        address indexed msgSender, address indexed srcSender, uint32 indexed chainId, uint256 amount
    );

    /**
     * @notice Emitted when a repay operation is executed
     */
    event mErc20Host_RepayExternal(
        address indexed msgSender, address indexed srcSender, address indexed position, uint32 chainId, uint256 amount
    );

    /**
     * @notice Emitted when a withdrawal is executed
     */
    event mErc20Host_WithdrawExternal(
        address indexed msgSender, address indexed srcSender, uint32 indexed chainId, uint256 amount
    );

    /**
     * @notice Emitted when a borrow operation is triggered for an extension chain
     */
    event mErc20Host_BorrowOnExternsionChain(address indexed sender, uint32 dstChainId, uint256 amount);

    /**
     * @notice Emitted when a withdraw operation is triggered for an extension chain
     */
    event mErc20Host_WithdrawOnExtensionChain(address indexed sender, uint32 dstChainId, uint256 amount);

    // ----------- ERRORS -----------
    /**
     * @notice Thrown when the chain id is not LINEA
     */
    error mErc20Host_ProofGenerationInputNotValid();

    /**
     * @notice Thrown when the dst chain id is not current chain
     */
    error mErc20Host_DstChainNotValid();

    /**
     * @notice Thrown when the chain id is not LINEA
     */
    error mErc20Host_ChainNotValid();

    /**
     * @notice Thrown when the address is not valid
     */
    error mErc20Host_AddressNotValid();

    /**
     * @notice Thrown when the amount provided is bigger than the available amount`
     */
    error mErc20Host_AmountTooBig();

    /**
     * @notice Thrown when the amount specified is invalid (e.g., zero)
     */
    error mErc20Host_AmountNotValid();

    /**
     * @notice Thrown when the journal data provided is invalid or corrupted
     */
    error mErc20Host_JournalNotValid();

    /**
     * @notice Thrown when caller is not allowed
     */
    error mErc20Host_CallerNotAllowed();

    /**
     * @notice Thrown when caller is not rebalancer
     */
    error mErc20Host_NotRebalancer();

    /**
     * @notice Thrown when length of array is not valid
     */
    error mErc20Host_LengthMismatch();

    // ----------- VIEW -----------
    /**
     * @notice Returns if a caller is allowed for sender
     */
    function isCallerAllowed(address sender, address caller) external view returns (bool);

    /**
     * @notice Returns the proof data journal
     */
    function getProofData(address user, uint32 dstId) external view returns (bytes memory);

    // ----------- PUBLIC -----------
    /**
     * @notice Extract amount to be used for rebalancing operation
     * @param amount The amount to rebalance
     */
    function extractForRebalancing(uint256 amount) external;

    /**
     * @notice Set caller status for `msg.sender`
     * @param caller The caller address
     * @param status The status to set for `caller`
     */
    function updateAllowedCallerStatus(address caller, bool status) external;

    /**
     * @notice Mints tokens after external verification
     * @param journalData The journal data for minting (array of encoded journals)
     * @param seal The Zk proof seal
     * @param userToLiquidate Array of positions to liquidate
     * @param liquidateAmount Array of amounts to liquidate
     * @param collateral Array of collaterals to seize
     * @param receiver The collateral receiver
     */
    function liquidateExternal(
        bytes calldata journalData,
        bytes calldata seal,
        address[] calldata userToLiquidate,
        uint256[] calldata liquidateAmount,
        address[] calldata collateral,
        address receiver
    ) external;

    /**
     * @notice Mints tokens after external verification
     * @param journalData The journal data for minting (array of encoded journals)
     * @param seal The Zk proof seal
     * @param mintAmount Array of amounts to mint
     * @param receiver The tokens receiver
     */
    function mintExternal(
        bytes calldata journalData,
        bytes calldata seal,
        uint256[] calldata mintAmount,
        address receiver
    ) external;

    /**
     * @notice Repays tokens after external verification
     * @param journalData The journal data for repayment (array of encoded journals)
     * @param seal The Zk proof seal
     * @param repayAmount Array of amounts to repay
     * @param receiver The position to repay for
     */
    function repayExternal(
        bytes calldata journalData,
        bytes calldata seal,
        uint256[] calldata repayAmount,
        address receiver
    ) external;

    /**
     * @notice Initiates a withdraw operation
     * @param amount The amount to withdraw
     * @param dstChainId The destination chain to recieve funds
     */
    function withdrawOnExtension(uint256 amount, uint32 dstChainId) external;

    /**
     * @notice Initiates a withdraw operation
     * @param amount The amount to withdraw
     * @param dstChainId The destination chain to recieve funds
     */
    function borrowOnExtension(uint256 amount, uint32 dstChainId) external;
}

File 9 of 25 : ImToken.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|                          
*/

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

interface ImTokenOperationTypes {
    enum OperationType {
        AmountIn,
        AmountInHere,
        AmountOut,
        AmountOutHere,
        Seize,
        Transfer,
        Mint,
        Borrow,
        Repay,
        Redeem,
        Liquidate
    }
}

interface ImTokenDelegator {
    /**
     * @notice Non-standard token able to delegate
     */
    function delegate(address delegatee) external;
}

interface ImTokenMinimal {
    /**
     * @notice EIP-20 token name for this token
     */
    function name() external view returns (string memory);

    /**
     * @notice EIP-20 token symbol for this token
     */
    function symbol() external view returns (string memory);

    /**
     * @notice EIP-20 token decimals for this token
     */
    function decimals() external view returns (uint8);

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

    /**
     * @notice Returns the value of tokens owned by `account`.
     * @param account The account to check for
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Returns true
     */
    function isMToken() external view returns (bool);

    /**
     * @dev Returns the underlying address
     */
    function underlying() external view returns (address);
}

interface ImToken is ImTokenMinimal {
    // ----------- STORAGE ------------
    /**
     * @notice Roles manager
     */
    function rolesOperator() external view returns (IRoles);

    /**
     * @notice Administrator for this contract
     */
    function admin() external view returns (address payable);

    /**
     * @notice Pending administrator for this contract
     */
    function pendingAdmin() external view returns (address payable);

    /**
     * @notice Contract which oversees inter-mToken operations
     */
    function operator() external view returns (address);

    /**
     * @notice Model which tells what the current interest rate should be
     */
    function interestRateModel() external view returns (address);

    /**
     * @notice Fraction of interest currently set aside for reserves
     */
    function reserveFactorMantissa() external view returns (uint256);

    /**
     * @notice Block number that interest was last accrued at
     */
    function accrualBlockNumber() external view returns (uint256);

    /**
     * @notice Accumulator of the total earned interest rate since the opening of the market
     */
    function borrowIndex() external view returns (uint256);

    /**
     * @notice Total amount of outstanding borrows of the underlying in this market
     */
    function totalBorrows() external view returns (uint256);

    /**
     * @notice Total amount of reserves of the underlying held in this market
     */
    function totalReserves() external view returns (uint256);

    // ----------- ACTIONS ------------
    /**
     * @notice Transfers `amount` tokens to the `dst` address
     * @param dst The address of the recipient
     * @param amount The number of tokens to transfer
     * @return Whether the transfer was successful or not
     */
    function transfer(address dst, uint256 amount) external returns (bool);

    /**
     * @notice Transfers `amount` tokens from the `src` address to the `dst` address
     * @param src The address from which tokens are transferred
     * @param dst The address to which tokens are transferred
     * @param amount The number of tokens to transfer
     * @return Whether the transfer was successful or not
     */
    function transferFrom(address src, address dst, uint256 amount) external returns (bool);

    /**
     * @notice Approves `spender` to spend `amount` tokens on behalf of the caller
     * @param spender The address authorized to spend tokens
     * @param amount The number of tokens to approve
     * @return Whether the approval was successful or not
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @notice Returns the current allowance the `spender` has from the `owner`
     * @param owner The address of the token holder
     * @param spender The address authorized to spend the tokens
     * @return The current remaining number of tokens `spender` can spend
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @notice Returns the balance of tokens held by `owner`
     * @param owner The address to query the balance for
     * @return The balance of tokens owned by `owner`
     */
    function balanceOf(address owner) external view returns (uint256);

    /**
     * @notice Returns the underlying asset balance of the `owner`
     * @param owner The address to query the balance of underlying assets for
     * @return The balance of underlying assets owned by `owner`
     */
    function balanceOfUnderlying(address owner) external returns (uint256);

    /**
     * @notice Returns the snapshot of account details for the given `account`
     * @param account The address to query the account snapshot for
     * @return (token balance, borrow balance, exchange rate)
     */
    function getAccountSnapshot(address account) external view returns (uint256, uint256, uint256);

    /**
     * @notice Returns the current borrow rate per block
     * @return The current borrow rate per block, scaled by 1e18
     */
    function borrowRatePerBlock() external view returns (uint256);

    /**
     * @notice Returns the current supply rate per block
     * @return The current supply rate per block, scaled by 1e18
     */
    function supplyRatePerBlock() external view returns (uint256);

    /**
     * @notice Returns the total amount of borrows, accounting for interest
     * @return The total amount of borrows
     */
    function totalBorrowsCurrent() external returns (uint256);

    /**
     * @notice Returns the current borrow balance for `account`, accounting for interest
     * @param account The address to query the borrow balance for
     * @return The current borrow balance
     */
    function borrowBalanceCurrent(address account) external returns (uint256);

    /**
     * @notice Returns the stored borrow balance for `account`, without accruing interest
     * @param account The address to query the stored borrow balance for
     * @return The stored borrow balance
     */
    function borrowBalanceStored(address account) external view returns (uint256);

    /**
     * @notice Returns the current exchange rate, with interest accrued
     * @return The current exchange rate
     */
    function exchangeRateCurrent() external returns (uint256);

    /**
     * @notice Returns the stored exchange rate, without accruing interest
     * @return The stored exchange rate
     */
    function exchangeRateStored() external view returns (uint256);

    /**
     * @notice Returns the total amount of available cash in the contract
     * @return The total amount of cash
     */
    function getCash() external view returns (uint256);

    /**
     * @notice Accrues interest on the contract's outstanding loans
     */
    function accrueInterest() external;

    /**
     * @notice Transfers collateral tokens (this market) to the liquidator.
     * @dev Will fail unless called by another mToken during the process of liquidation.
     *  Its absolutely critical to use msg.sender as the borrowed mToken and not a parameter.
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of mTokens to seize
     */
    function seize(address liquidator, address borrower, uint256 seizeTokens) external;

    /**
     * @notice Accrues interest and reduces reserves by transferring to admin
     * @param reduceAmount Amount of reduction to reserves
     */
    function reduceReserves(uint256 reduceAmount) external;
}

File 10 of 25 : IRoles.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

interface IRoles {
    error Roles_InputNotValid();

    /**
     * @notice Returns REBALANCER role
     */
    function REBALANCER() external view returns (bytes32);

    /**
     * @notice Returns REBALANCER_EOA role
     */
    function REBALANCER_EOA() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_PAUSE role
     */
    function GUARDIAN_PAUSE() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_TRANSFER role
     */
    function GUARDIAN_TRANSFER() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_SEIZE role
     */
    function GUARDIAN_SEIZE() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_MINT role
     */
    function GUARDIAN_MINT() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_BORROW role
     */
    function GUARDIAN_BORROW() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_BRIDGE role
     */
    function GUARDIAN_BRIDGE() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_BORROW_CAP role
     */
    function GUARDIAN_BORROW_CAP() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_SUPPLY_CAP role
     */
    function GUARDIAN_SUPPLY_CAP() external view returns (bytes32);

    /**
     * @notice Returns GUARDIAN_RESERVE role
     */
    function GUARDIAN_RESERVE() external view returns (bytes32);

    /**
     * @notice Returns PROOF_FORWARDER role
     */
    function PROOF_FORWARDER() external view returns (bytes32);

    /**
     * @notice Returns PROOF_BATCH_FORWARDER role
     */
    function PROOF_BATCH_FORWARDER() external view returns (bytes32);

    /**
     * @notice Returns PAUSE_MANAGER role
     */
    function PAUSE_MANAGER() external view returns (bytes32);

    /**
     * @notice Returns CHAINS_MANAGER role
     */
    function CHAINS_MANAGER() external view returns (bytes32);

    /**
     * @notice Returns allowance status for a contract and a role
     * @param _contract the contract address
     * @param _role the bytes32 role
     */
    function isAllowedFor(address _contract, bytes32 _role) external view returns (bool);
}

File 11 of 25 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 12 of 25 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

File 13 of 25 : IRiscZeroVerifier.sol
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.9;

import {reverseByteOrderUint32} from "./Util.sol";

/// @notice A receipt attesting to the execution of a guest program.
/// @dev A receipt contains two parts: a seal and a claim. The seal is a zero-knowledge proof
/// attesting to knowledge of a zkVM execution resulting in the claim. The claim is a set of public
/// outputs for the execution. Crucially, the claim includes the journal and the image ID. The
/// image ID identifies the program that was executed, and the journal is the public data written
/// by the program. Note that this struct only contains the claim digest, as can be obtained with
/// the `digest()` function on `ReceiptClaimLib`.
struct Receipt {
    bytes seal;
    bytes32 claimDigest;
}

/// @notice Public claims about a zkVM guest execution, such as the journal committed to by the guest.
/// @dev Also includes important information such as the exit code and the starting and ending system
/// state (i.e. the state of memory). `ReceiptClaim` is a "Merkle-ized struct" supporting
/// partial openings of the underlying fields from a hash commitment to the full structure.
struct ReceiptClaim {
    /// @notice Digest of the SystemState just before execution has begun.
    bytes32 preStateDigest;
    /// @notice Digest of the SystemState just after execution has completed.
    bytes32 postStateDigest;
    /// @notice The exit code for the execution.
    ExitCode exitCode;
    /// @notice A digest of the input to the guest.
    /// @dev This field is currently unused and must be set to the zero digest.
    bytes32 input;
    /// @notice Digest of the Output of the guest, including the journal
    /// and assumptions set during execution.
    bytes32 output;
}

library ReceiptClaimLib {
    using OutputLib for Output;
    using SystemStateLib for SystemState;

    bytes32 constant TAG_DIGEST = sha256("risc0.ReceiptClaim");

    // Define a constant to ensure hashing is done at compile time. Can't use the
    // SystemStateLib.digest method here because the Solidity compiler complains.
    bytes32 constant SYSTEM_STATE_ZERO_DIGEST = 0xa3acc27117418996340b84e5a90f3ef4c49d22c79e44aad822ec9c313e1eb8e2;

    /// @notice Construct a ReceiptClaim from the given imageId and journalDigest.
    ///         Returned ReceiptClaim will represent a successful execution of the zkVM, running
    ///         the program committed by imageId and resulting in the journal specified by
    ///         journalDigest.
    /// @param imageId The identifier for the guest program.
    /// @param journalDigest The SHA-256 digest of the journal bytes.
    /// @dev Input hash and postStateDigest are set to all-zeros (i.e. no committed input, or
    ///      final memory state), the exit code is (Halted, 0), and there are no assumptions
    ///      (i.e. the receipt is unconditional).
    function ok(bytes32 imageId, bytes32 journalDigest) internal pure returns (ReceiptClaim memory) {
        return ReceiptClaim(
            imageId,
            SYSTEM_STATE_ZERO_DIGEST,
            ExitCode(SystemExitCode.Halted, 0),
            bytes32(0),
            Output(journalDigest, bytes32(0)).digest()
        );
    }

    function digest(ReceiptClaim memory claim) internal pure returns (bytes32) {
        return sha256(
            abi.encodePacked(
                TAG_DIGEST,
                // down
                claim.input,
                claim.preStateDigest,
                claim.postStateDigest,
                claim.output,
                // data
                uint32(claim.exitCode.system) << 24,
                uint32(claim.exitCode.user) << 24,
                // down.length
                uint16(4) << 8
            )
        );
    }
}

/// @notice Commitment to the memory state and program counter (pc) of the zkVM.
/// @dev The "pre" and "post" fields of the ReceiptClaim are digests of the system state at the
///      start are stop of execution. Programs are loaded into the zkVM by creating a memory image
///      of the loaded program, and creating a system state for initializing the zkVM. This is
///      known as the "image ID".
struct SystemState {
    /// @notice Program counter.
    uint32 pc;
    /// @notice Root hash of a merkle tree which confirms the integrity of the memory image.
    bytes32 merkle_root;
}

library SystemStateLib {
    bytes32 constant TAG_DIGEST = sha256("risc0.SystemState");

    function digest(SystemState memory state) internal pure returns (bytes32) {
        return sha256(
            abi.encodePacked(
                TAG_DIGEST,
                // down
                state.merkle_root,
                // data
                reverseByteOrderUint32(state.pc),
                // down.length
                uint16(1) << 8
            )
        );
    }
}

/// @notice Exit condition indicated by the zkVM at the end of the guest execution.
/// @dev Exit codes have a "system" part and a "user" part. Semantically, the system part is set to
/// indicate the type of exit (e.g. halt, pause, or system split) and is directly controlled by the
/// zkVM. The user part is an exit code, similar to exit codes used in Linux, chosen by the guest
/// program to indicate additional information (e.g. 0 to indicate success or 1 to indicate an
/// error).
struct ExitCode {
    SystemExitCode system;
    uint8 user;
}

/// @notice Exit condition indicated by the zkVM at the end of the execution covered by this proof.
/// @dev
/// `Halted` indicates normal termination of a program with an interior exit code returned from the
/// guest program. A halted program cannot be resumed.
///
/// `Paused` indicates the execution ended in a paused state with an interior exit code set by the
/// guest program. A paused program can be resumed such that execution picks up where it left
/// of, with the same memory state.
///
/// `SystemSplit` indicates the execution ended on a host-initiated system split. System split is
/// mechanism by which the host can temporarily stop execution of the execution ended in a system
/// split has no output and no conclusions can be drawn about whether the program will eventually
/// halt. System split is used in continuations to split execution into individually provable segments.
enum SystemExitCode {
    Halted,
    Paused,
    SystemSplit
}

/// @notice Output field in the `ReceiptClaim`, committing to a claimed journal and assumptions list.
struct Output {
    /// @notice Digest of the journal committed to by the guest execution.
    bytes32 journalDigest;
    /// @notice Digest of the ordered list of `ReceiptClaim` digests corresponding to the
    /// calls to `env::verify` and `env::verify_integrity`.
    /// @dev Verifying the integrity of a `Receipt` corresponding to a `ReceiptClaim` with a
    /// non-empty assumptions list does not guarantee unconditionally any of the claims over the
    /// guest execution (i.e. if the assumptions list is non-empty, then the journal digest cannot
    /// be trusted to correspond to a genuine execution). The claims can be checked by additional
    /// verifying a `Receipt` for every digest in the assumptions list.
    bytes32 assumptionsDigest;
}

library OutputLib {
    bytes32 constant TAG_DIGEST = sha256("risc0.Output");

    function digest(Output memory output) internal pure returns (bytes32) {
        return sha256(
            abi.encodePacked(
                TAG_DIGEST,
                // down
                output.journalDigest,
                output.assumptionsDigest,
                // down.length
                uint16(2) << 8
            )
        );
    }
}

/// @notice Error raised when cryptographic verification of the zero-knowledge proof fails.
error VerificationFailed();

/// @notice Verifier interface for RISC Zero receipts of execution.
interface IRiscZeroVerifier {
    /// @notice Verify that the given seal is a valid RISC Zero proof of execution with the
    ///     given image ID and journal digest. Reverts on failure.
    /// @dev This method additionally ensures that the input hash is all-zeros (i.e. no
    /// committed input), the exit code is (Halted, 0), and there are no assumptions (i.e. the
    /// receipt is unconditional).
    /// @param seal The encoded cryptographic proof (i.e. SNARK).
    /// @param imageId The identifier for the guest program.
    /// @param journalDigest The SHA-256 digest of the journal bytes.
    function verify(bytes calldata seal, bytes32 imageId, bytes32 journalDigest) external view;

    /// @notice Verify that the given receipt is a valid RISC Zero receipt, ensuring the `seal` is
    /// valid a cryptographic proof of the execution with the given `claim`. Reverts on failure.
    /// @param receipt The receipt to be verified.
    function verifyIntegrity(Receipt calldata receipt) external view;
}

File 14 of 25 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 15 of 25 : mErc20.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {ImErc20} from "src/interfaces/ImErc20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ImTokenMinimal, ImTokenDelegator} from "src/interfaces/ImToken.sol";

// contracts
import {mToken} from "./mToken.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
 * @title Malda's mErc20 Contract
 * @notice mTokens which wrap an EIP-20 underlying
 */
contract mErc20 is mToken, ImErc20 {
    using SafeERC20 for IERC20;

    // ----------- STORAGE ------------
    /**
     * @notice Underlying asset for this mToken
     */
    address public underlying;

    // ----------- ERRORS ------------
    error mErc20_TokenNotValid();

    /**
     * @notice Initialize the new money market
     * @param underlying_ The address of the underlying asset
     * @param operator_ The address of the Operator
     * @param interestRateModel_ The address of the interest rate model
     * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
     * @param name_ ERC-20 name of this token
     * @param symbol_ ERC-20 symbol of this token
     * @param decimals_ ERC-20 decimal precision of this token
     */
    function initialize(
        address underlying_,
        address operator_,
        address interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) public {
        // mToken initialize does the bulk of the work
        super.initialize(operator_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);

        // Set underlying and sanity check it
        underlying = underlying_;
        ImTokenMinimal(underlying).totalSupply();
    }

    // ----------- OWNER ------------
    /**
     * @notice Admin call to delegate the votes of the MALDA-like underlying
     * @param delegatee The address to delegate votes to
     * @dev mTokens whose underlying are not  should revert here
     */
    function delegateMaldaLikeTo(address delegatee) external onlyAdmin {
        ImTokenDelegator(underlying).delegate(delegatee);
    }

    /**
     * @notice A public function to sweep accidental ERC-20 transfers to this contract. Tokens are sent to admin (timelock)
     * @param token The address of the ERC-20 token to sweep
     */
    function sweepToken(IERC20 token) external onlyAdmin {
        require(address(token) != underlying, mErc20_TokenNotValid());
        uint256 balance = token.balanceOf(address(this));
        token.safeTransfer(admin, balance);
    }

    // ----------- MARKET PUBLIC ------------
    /**
     * @inheritdoc ImErc20
     */
    function mint(uint256 mintAmount) external {
        _mint(msg.sender, mintAmount, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function redeem(uint256 redeemTokens) external {
        _redeem(msg.sender, redeemTokens, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function redeemUnderlying(uint256 redeemAmount) external {
        _redeemUnderlying(msg.sender, redeemAmount, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function borrow(uint256 borrowAmount) external {
        _borrow(msg.sender, borrowAmount, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function repay(uint256 repayAmount) external {
        _repay(repayAmount, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function repayBehalf(address borrower, uint256 repayAmount) external {
        _repayBehalf(borrower, repayAmount, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function liquidate(address borrower, uint256 repayAmount, address mTokenCollateral) external {
        _liquidate(msg.sender, borrower, repayAmount, mTokenCollateral, true);
    }

    /**
     * @inheritdoc ImErc20
     */
    function addReserves(uint256 addAmount) external {
        return _addReserves(addAmount);
    }

    // ----------- INTERNAL ------------
    /**
     * @notice Gets balance of this contract in terms of the underlying
     * @dev This excludes the value of the current message, if any
     * @return The quantity of underlying tokens owned by this contract
     */
    function _getCashPrior() internal view virtual override returns (uint256) {
        return initialExchangeRateMantissa;
    }

    /**
     * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
     *  This may revert due to insufficient balance or insufficient allowance.
     */
    function _doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) {
        uint256 balanceBefore = IERC20(underlying).balanceOf(address(this));
        IERC20(underlying).safeTransferFrom(from, address(this), amount);
        uint256 balanceAfter = IERC20(underlying).balanceOf(address(this));
        return balanceAfter - balanceBefore;
    }

    /**
     * @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting.
     *  If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
     *  If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
     */
    function _doTransferOut(address payable to, uint256 amount) internal virtual override {
        IERC20(underlying).safeTransfer(to, amount);
    }
}

File 16 of 25 : BytesLib.sol
// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
*/
pragma solidity =0.8.28;

library BytesLib {
    function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for { let cc := add(_postBytes, 0x20) } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } { mstore(mc, mload(cc)) }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(
                0x40,
                and(
                    add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                    not(31) // Round down to the nearest 32 bytes.
                )
            )
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } { sstore(sc, mload(mc)) }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } { sstore(sc, mload(mc)) }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(bytes memory _bytes, uint256 _start, uint256 _length) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } { mstore(mc, mload(cc)) }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for { let cc := add(_postBytes, 0x20) }
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let endMinusWord := add(_preBytes, length)
                let mc := add(_preBytes, 0x20)
                let cc := add(_postBytes, 0x20)

                for {
                    // the next line is the loop condition:
                    // while(uint256(mc < endWord) + cb == 2)
                } eq(add(lt(mc, endMinusWord), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }

                // Only if still successful
                // For <1 word tail bytes
                if gt(success, 0) {
                    // Get the remainder of length/32
                    // length % 32 = AND(length, 32 - 1)
                    let numTailBytes := and(length, 0x1f)
                    let mcRem := mload(mc)
                    let ccRem := mload(cc)
                    for { let i := 0 }
                    // the next line is the loop condition:
                    // while(uint256(i < numTailBytes) + cb == 2)
                    eq(add(lt(i, numTailBytes), cb), 2) { i := add(i, 1) } {
                        if iszero(eq(byte(i, mcRem), byte(i, ccRem))) {
                            // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

File 17 of 25 : Util.sol
// Copyright 2024 RISC Zero, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.9;

/// @notice reverse the byte order of the uint256 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint256(uint256 input) pure returns (uint256 v) {
    v = input;

    // swap bytes
    v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >> 8)
        | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);

    // swap 2-byte long pairs
    v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >> 16)
        | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);

    // swap 4-byte long pairs
    v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >> 32)
        | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);

    // swap 8-byte long pairs
    v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >> 64)
        | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);

    // swap 16-byte long pairs
    v = (v >> 128) | (v << 128);
}

/// @notice reverse the byte order of the uint32 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint32(uint32 input) pure returns (uint32 v) {
    v = input;

    // swap bytes
    v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);

    // swap 2-byte long pairs
    v = (v >> 16) | (v << 16);
}

/// @notice reverse the byte order of the uint16 value.
/// @dev Solidity uses a big-endian ABI encoding. Reversing the byte order before encoding
/// ensure that the encoded value will be little-endian.
/// Written by k06a. https://ethereum.stackexchange.com/a/83627
function reverseByteOrderUint16(uint16 input) pure returns (uint16 v) {
    v = input;

    // swap bytes
    v = (v >> 8) | ((v & 0x00FF) << 8);
}

File 18 of 25 : ImErc20.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|                          
*/

interface ImErc20 {
    /**
     * @notice Sender supplies assets into the market and receives mTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param mintAmount The amount of the underlying asset to supply
     */
    function mint(uint256 mintAmount) external;

    /**
     * @notice Sender redeems mTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemTokens The number of mTokens to redeem into underlying
     */
    function redeem(uint256 redeemTokens) external;

    /**
     * @notice Sender redeems mTokens in exchange for a specified amount of underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemAmount The amount of underlying to redeem
     */
    function redeemUnderlying(uint256 redeemAmount) external;

    /**
     * @notice Sender borrows assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     */
    function borrow(uint256 borrowAmount) external;

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     */
    function repay(uint256 repayAmount) external;

    /**
     * @notice Sender repays a borrow belonging to borrower
     * @param borrower the account with the debt being payed off
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     */
    function repayBehalf(address borrower, uint256 repayAmount) external;

    /**
     * @notice The sender liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this mToken to be liquidated
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @param mTokenCollateral The market in which to seize collateral from the borrower
     */
    function liquidate(address borrower, uint256 repayAmount, address mTokenCollateral) external;

    /**
     * @notice The sender adds to reserves.
     * @param addAmount The amount fo underlying token to add as reserves
     */
    function addReserves(uint256 addAmount) external;
}

File 19 of 25 : mToken.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {ImToken, ImTokenMinimal} from "src/interfaces/ImToken.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";
import {IOperator, IOperatorDefender} from "src/interfaces/IOperator.sol";

// contracts
import {mTokenConfiguration} from "./mTokenConfiguration.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

abstract contract mToken is mTokenConfiguration, ReentrancyGuard {
    /**
     * @notice Initialize the money market
     * @param operator_ The address of the Operator
     * @param interestRateModel_ The address of the interest rate model
     * @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18
     * @param name_ EIP-20 name of this token
     * @param symbol_ EIP-20 symbol of this token
     * @param decimals_ EIP-20 decimal precision of this token
     */
    function initialize(
        address operator_,
        address interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) public onlyAdmin {
        require(accrualBlockNumber == 0 && borrowIndex == 0, mToken_AlreadyInitialized());
        require(initialExchangeRateMantissa_ > 0, mToken_ExchangeRateNotValid());
        // Set initial exchange rate
        initialExchangeRateMantissa = initialExchangeRateMantissa_;

        _setOperator(operator_);

        accrualBlockNumber = _getBlockNumber();
        borrowIndex = mantissaOne;

        _setInterestRateModel(interestRateModel_);

        name = name_;
        symbol = symbol_;
        decimals = decimals_;
    }

    function isMToken() external pure override returns (bool) {
        return true;
    }

    // ----------- TOKENS VIEW ------------
    /**
     * @inheritdoc ImToken
     */
    function allowance(address owner, address spender) external view override returns (uint256) {
        return transferAllowances[owner][spender];
    }

    /**
     * @inheritdoc ImTokenMinimal
     */
    function balanceOf(address owner) external view override returns (uint256) {
        return accountTokens[owner];
    }

    /**
     * @inheritdoc ImToken
     */
    function balanceOfUnderlying(address owner) external override returns (uint256) {
        Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
        return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);
    }

    // ----------- MARKETS VIEW ------------
    /**
     * @inheritdoc ImToken
     */
    function getAccountSnapshot(address account) external view override returns (uint256, uint256, uint256) {
        return (accountTokens[account], _borrowBalanceStored(account), _exchangeRateStored());
    }

    /**
     * @inheritdoc ImToken
     */
    function borrowRatePerBlock() external view override returns (uint256) {
        return IInterestRateModel(interestRateModel).getBorrowRate(_getCashPrior(), totalBorrows, totalReserves);
    }

    /**
     * @inheritdoc ImToken
     */
    function supplyRatePerBlock() external view override returns (uint256) {
        return IInterestRateModel(interestRateModel).getSupplyRate(
            _getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa
        );
    }

    /**
     * @inheritdoc ImToken
     */
    function borrowBalanceStored(address account) external view override returns (uint256) {
        return _borrowBalanceStored(account);
    }

    /**
     * @inheritdoc ImToken
     */
    function getCash() external view override returns (uint256) {
        return _getCashPrior();
    }

    /**
     * @inheritdoc ImToken
     */
    function exchangeRateStored() external view override returns (uint256) {
        return _exchangeRateStored();
    }

    // ----------- TOKENS PUBLIC ------------
    /**
     * @inheritdoc ImToken
     */
    function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {
        _transferTokens(msg.sender, msg.sender, dst, amount);

        return true;
    }

    /**
     * @inheritdoc ImToken
     */
    function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {
        _transferTokens(msg.sender, src, dst, amount);

        return true;
    }

    /**
     * @inheritdoc ImToken
     */
    function approve(address spender, uint256 amount) external override returns (bool) {
        address src = msg.sender;
        transferAllowances[src][spender] = amount;
        emit Approval(src, spender, amount);
        return true;
    }

    // ----------- MARKETS PUBLIC ------------
    /**
     * @inheritdoc ImToken
     */
    function totalBorrowsCurrent() external override nonReentrant returns (uint256) {
        _accrueInterest();
        return totalBorrows;
    }

    /**
     * @inheritdoc ImToken
     */
    function borrowBalanceCurrent(address account) external override nonReentrant returns (uint256) {
        _accrueInterest();
        return _borrowBalanceStored(account);
    }

    /**
     * @inheritdoc ImToken
     */
    function exchangeRateCurrent() public override nonReentrant returns (uint256) {
        _accrueInterest();
        return _exchangeRateStored();
    }

    /**
     * @inheritdoc ImToken
     */
    function seize(address liquidator, address borrower, uint256 seizeTokens) external override nonReentrant {
        _seize(msg.sender, liquidator, borrower, seizeTokens);
    }

    /**
     * @inheritdoc ImToken
     */
    function reduceReserves(uint256 reduceAmount) external override nonReentrant {
        require(
            msg.sender == admin || rolesOperator.isAllowedFor(msg.sender, rolesOperator.GUARDIAN_RESERVE()),
            mToken_OnlyAdminOrRole()
        );

        _accrueInterest();

        require(_getCashPrior() >= reduceAmount, mToken_ReserveCashNotAvailable());
        require(reduceAmount <= totalReserves, mToken_ReserveCashNotAvailable());

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)
        // totalReserves - reduceAmount
        uint256 totalReservesNew = totalReserves - reduceAmount;

        // Store reserves[n+1] = reserves[n] - reduceAmount
        totalReserves = totalReservesNew;

        // doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
        _doTransferOut(payable(msg.sender), reduceAmount);
        initialExchangeRateMantissa -= reduceAmount;

        emit ReservesReduced(admin, reduceAmount, totalReservesNew);
    }

    // ----------- INTERNAL VIEW ------------
    /**
     * @notice Return the borrow balance of account based on stored data
     * @param account The address whose balance should be calculated
     * @return (error code, the calculated balance or 0 if error code is non-zero)
     */
    function _borrowBalanceStored(address account) internal view returns (uint256) {
        /* Get borrowBalance and borrowIndex */
        BorrowSnapshot storage borrowSnapshot = accountBorrows[account];

        /* If borrowBalance = 0 then borrowIndex is likely also 0.
         * Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
         */
        if (borrowSnapshot.principal == 0) {
            return 0;
        }

        /* Calculate new borrow balance using the interest index:
         *  recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
         */
        uint256 principalTimesIndex = borrowSnapshot.principal * borrowIndex;
        return principalTimesIndex / borrowSnapshot.interestIndex;
    }

    // ----------- INTERNAL ------------
    /**
     * @notice Sender supplies assets into the market and receives mTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param user The user address
     * @param mintAmount The amount of the underlying asset to supply
     * @param doTransfer If an actual transfer should be performed
     */
    function _mint(address user, uint256 mintAmount, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits the actual Mint event if successful and logs on errors, so we don't need to
        __mint(user, mintAmount, doTransfer);
    }

    /**
     * @notice Sender redeems mTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param user The user address
     * @param redeemTokens The number of mTokens to redeem into underlying
     * @param doTransfer If an actual transfer should be performed
     */
    function _redeem(address user, uint256 redeemTokens, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits redeem-specific logs on errors, so we don't need to
        __redeem(payable(user), redeemTokens, 0, doTransfer);
    }

    /**
     * @notice Sender redeems mTokens in exchange for a specified amount of underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param user The user address
     * @param redeemAmount The amount of underlying to receive from redeeming mTokens
     * @param doTransfer If an actual transfer should be performed
     */
    function _redeemUnderlying(address user, uint256 redeemAmount, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits redeem-specific logs on errors, so we don't need to
        __redeem(payable(user), 0, redeemAmount, doTransfer);
    }

    /**
     * @notice Sender borrows assets from the protocol to their own address
     * @param user The user address
     * @param borrowAmount The amount of the underlying asset to borrow
     * @param doTransfer If an actual transfer should be performed
     */
    function _borrow(address user, uint256 borrowAmount, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits borrow-specific logs on errors, so we don't need to
        __borrow(payable(user), borrowAmount, doTransfer);
    }

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     * @param doTransfer If an actual transfer should be performed
     */
    function _repay(uint256 repayAmount, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits repay-borrow-specific logs on errors, so we don't need to
        __repay(msg.sender, msg.sender, repayAmount, doTransfer);
    }

    /**
     * @notice Sender repays a borrow belonging to borrower
     * @param borrower the account with the debt being payed off
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     * @param doTransfer If an actual transfer should be performed
     */
    function _repayBehalf(address borrower, uint256 repayAmount, bool doTransfer) internal nonReentrant {
        _accrueInterest();
        // emits repay-borrow-specific logs on errors, so we don't need to
        __repay(msg.sender, borrower, repayAmount, doTransfer);
    }

    /**
     * @notice The sender liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The liquidator address
     * @param borrower The borrower of this mToken to be liquidated
     * @param mTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @param doTransfer If an actual transfer should be performed
     */
    function _liquidate(
        address liquidator,
        address borrower,
        uint256 repayAmount,
        address mTokenCollateral,
        bool doTransfer
    ) internal nonReentrant {
        _accrueInterest();

        ImToken(mTokenCollateral).accrueInterest();

        // emits borrow-specific logs on errors, so we don't need to
        __liquidate(liquidator, borrower, repayAmount, mTokenCollateral, doTransfer);
    }

    /**
     * @notice Transfers collateral tokens (this market) to the liquidator.
     * @dev Called only during an in-kind liquidation, or by liquidateBorrow during the liquidation of another mToken.
     *  Its absolutely critical to use msg.sender as the seizer mToken and not a parameter.
     * @param seizerToken The contract seizing the collateral (i.e. borrowed mToken)
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of mTokens to seize
     */
    function _seize(address seizerToken, address liquidator, address borrower, uint256 seizeTokens) internal {
        IOperatorDefender(operator).beforeMTokenSeize(address(this), seizerToken, liquidator, borrower);

        require(borrower != liquidator, mToken_InvalidInput());

        /*
         * We calculate the new borrower and liquidator token balances, failing on underflow/overflow:
         *  borrowerTokensNew = accountTokens[borrower] - seizeTokens
         *  liquidatorTokensNew = accountTokens[liquidator] + seizeTokens
         */
        uint256 protocolSeizeTokens = mul_(seizeTokens, Exp({mantissa: PROTOCOL_SEIZE_SHARE_MANTISSA}));
        uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
        Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});
        uint256 protocolSeizeAmount = mul_ScalarTruncate(exchangeRate, protocolSeizeTokens);
        uint256 totalReservesNew = totalReserves + protocolSeizeAmount;

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We write the calculated values into storage */
        totalReserves = totalReservesNew;
        totalSupply = totalSupply - protocolSeizeTokens;
        accountTokens[borrower] = accountTokens[borrower] - seizeTokens;
        accountTokens[liquidator] = accountTokens[liquidator] + liquidatorSeizeTokens;

        /* Emit a Transfer event */
        emit Transfer(borrower, liquidator, liquidatorSeizeTokens);
        emit Transfer(borrower, address(this), protocolSeizeTokens);
        emit ReservesAdded(address(this), protocolSeizeAmount, totalReservesNew);
    }

    /**
     * @notice Accrues interest and reduces reserves by transferring from msg.sender
     * @param addAmount Amount of addition to reserves
     */
    function _addReserves(uint256 addAmount) internal nonReentrant {
        _accrueInterest();

        // totalReserves + actualAddAmount
        uint256 totalReservesNew;
        uint256 actualAddAmount;

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We call doTransferIn for the caller and the addAmount
         *  Note: The mToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the mToken holds an additional addAmount of cash.
         *  doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
         *  it returns the amount actually transferred, in case of a fee.
         */

        actualAddAmount = _doTransferIn(msg.sender, addAmount);
        initialExchangeRateMantissa += actualAddAmount;

        totalReservesNew = totalReserves + actualAddAmount;

        // Store reserves[n+1] = reserves[n] + actualAddAmount
        totalReserves = totalReservesNew;

        /* Emit NewReserves(admin, actualAddAmount, reserves[n+1]) */
        emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);
    }

    // ----------- PRIVATE ------------
    /**
     * @notice The liquidator liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param liquidator The address repaying the borrow and seizing collateral
     * @param borrower The borrower of this mToken to be liquidated
     * @param mTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @param doTransfer If an actual transfer should be performed
     */
    function __liquidate(
        address liquidator,
        address borrower,
        uint256 repayAmount,
        address mTokenCollateral,
        bool doTransfer
    ) internal {
        require(borrower != liquidator, mToken_InvalidInput());
        require(repayAmount > 0 && repayAmount != type(uint256).max, mToken_InvalidInput());

        IOperatorDefender(operator).beforeMTokenLiquidate(address(this), mTokenCollateral, borrower, repayAmount);

        require(
            ImToken(mTokenCollateral).accrualBlockNumber() == _getBlockNumber(), mToken_CollateralBlockNumberNotValid()
        );

        /* Fail if repayBorrow fails */
        uint256 actualRepayAmount = __repay(liquidator, borrower, repayAmount, doTransfer);

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We calculate the number of collateral tokens that will be seized */
        uint256 seizeTokens =
            IOperator(operator).liquidateCalculateSeizeTokens(address(this), mTokenCollateral, actualRepayAmount);

        /* Revert if borrower collateral token balance < seizeTokens */
        require(ImToken(mTokenCollateral).balanceOf(borrower) >= seizeTokens, mToken_LiquidateSeizeTooMuch());

        // If this is also the collateral, run _seize to avoid re-entrancy, otherwise make an external call
        if (address(mTokenCollateral) == address(this)) {
            _seize(address(this), liquidator, borrower, seizeTokens);
        } else {
            ImToken(mTokenCollateral).seize(liquidator, borrower, seizeTokens);
        }

        /* We emit a LiquidateBorrow event */
        emit LiquidateBorrow(liquidator, borrower, actualRepayAmount, address(mTokenCollateral), seizeTokens);
    }
    /**
     * @notice Borrows are repaid by another user (possibly the borrower).
     * @param payer the account paying off the borrow
     * @param borrower the account with the debt being payed off
     * @param repayAmount the amount of underlying tokens being returned, or -1 for the full outstanding amount
     * @param doTransfer If an actual transfer should be performed
     */

    function __repay(address payer, address borrower, uint256 repayAmount, bool doTransfer) private returns (uint256) {
        IOperatorDefender(operator).beforeMTokenRepay(address(this), borrower);

        /* We fetch the amount the borrower owes, with accumulated interest */
        uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);

        /* If repayAmount == type(uint256).max , repayAmount = accountBorrows */
        uint256 repayAmountFinal = repayAmount == type(uint256).max ? accountBorrowsPrev : repayAmount;

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We call _doTransferIn for the payer and the repayAmount
         *  Note: The mToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the mToken holds an additional repayAmount of cash.
         *  _doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
         *   it returns the amount actually transferred, in case of a fee.
         */
        uint256 actualRepayAmount = doTransfer ? _doTransferIn(payer, repayAmountFinal) : repayAmountFinal;
        initialExchangeRateMantissa += actualRepayAmount;

        /*
         * We calculate the new borrower and total borrow balances, failing on underflow:
         *  accountBorrowsNew = accountBorrows - actualRepayAmount
         *  totalBorrowsNew = totalBorrows - actualRepayAmount
         */
        uint256 accountBorrowsNew = accountBorrowsPrev - actualRepayAmount;
        uint256 totalBorrowsNew = totalBorrows - actualRepayAmount;

        /* We write the previously calculated values into storage */
        accountBorrows[borrower].principal = accountBorrowsNew;
        accountBorrows[borrower].interestIndex = borrowIndex;
        totalBorrows = totalBorrowsNew;

        /* We emit a RepayBorrow event */
        emit RepayBorrow(payer, borrower, actualRepayAmount, accountBorrowsNew, totalBorrowsNew);

        return actualRepayAmount;
    }

    /**
     * @notice Users borrow assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     */
    function __borrow(address payable borrower, uint256 borrowAmount, bool doTransfer) private {
        IOperatorDefender(operator).beforeMTokenBorrow(address(this), borrower, borrowAmount);

        require(_getCashPrior() >= borrowAmount, mToken_BorrowCashNotAvailable());

        /*
         * We calculate the new borrower and total borrow balances, failing on overflow:
         *  accountBorrowNew = accountBorrow + borrowAmount
         *  totalBorrowsNew = totalBorrows + borrowAmount
         */
        uint256 accountBorrowsPrev = _borrowBalanceStored(borrower);
        uint256 accountBorrowsNew = accountBorrowsPrev + borrowAmount;
        uint256 totalBorrowsNew = totalBorrows + borrowAmount;

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We write the previously calculated values into storage.
         *  Note: Avoid token reentrancy attacks by writing increased borrow before external transfer.
        `*/
        accountBorrows[borrower].principal = accountBorrowsNew;
        accountBorrows[borrower].interestIndex = borrowIndex;
        totalBorrows = totalBorrowsNew;

        if (doTransfer) {
            /*
            * We invoke _doTransferOut for the borrower and the borrowAmount.
            *  Note: The mToken must handle variations between ERC-20 and ETH underlying.
            *  On success, the mToken borrowAmount less of cash.
            *  _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
            */
            _doTransferOut(borrower, borrowAmount);
        }
        initialExchangeRateMantissa -= borrowAmount;

        /* We emit a Borrow event */
        emit Borrow(borrower, borrowAmount, accountBorrowsNew, totalBorrowsNew);
    }

    function __redeem(address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn, bool doTransfer)
        private
    {
        require(redeemTokensIn == 0 || redeemAmountIn == 0, mToken_InvalidInput());

        /* exchangeRate = invoke Exchange Rate Stored() */
        Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});

        uint256 redeemTokens;
        uint256 redeemAmount;
        /* If redeemTokensIn > 0: */
        if (redeemTokensIn > 0) {
            /*
             * We calculate the exchange rate and the amount of underlying to be redeemed:
             *  redeemTokens = redeemTokensIn
             *  redeemAmount = redeemTokensIn x exchangeRateCurrent
             */
            redeemTokens = redeemTokensIn;
            redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
        } else {
            /*
             * We get the current exchange rate and calculate the amount to be redeemed:
             *  redeemTokens = redeemAmountIn / exchangeRate
             *  redeemAmount = redeemAmountIn
             */
            redeemTokens = div_(redeemAmountIn, exchangeRate);
            redeemAmount = redeemAmountIn;
        }
        if (redeemTokens == 0 && redeemAmount == 0) revert mToken_RedeemEmpty();

        /* Fail if redeem not allowed */
        IOperatorDefender(operator).beforeMTokenRedeem(address(this), redeemer, redeemTokens);

        require(_getCashPrior() >= redeemAmount, mToken_RedeemCashNotAvailable());

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         * We write the previously calculated values into storage.
         *  Note: Avoid token reentrancy attacks by writing reduced supply before external transfer.
         */
        totalSupply = totalSupply - redeemTokens;
        accountTokens[redeemer] = accountTokens[redeemer] - redeemTokens;

        /*
         * We invoke _doTransferOut for the redeemer and the redeemAmount.
         *  Note: The mToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the mToken has redeemAmount less of cash.
         *  _doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
         */
        if (doTransfer) _doTransferOut(redeemer, redeemAmount);
        initialExchangeRateMantissa -= redeemAmount;

        /* We emit a Transfer event, and a Redeem event */
        emit Transfer(redeemer, address(this), redeemTokens);
        emit Redeem(redeemer, redeemAmount, redeemTokens);
    }
    /**
     * @notice User supplies assets into the market and receives mTokens in exchange
     * @dev Assumes interest has already been accrued up to the current block
     * @param minter The address of the account which is supplying the assets
     * @param mintAmount The amount of the underlying asset to supply
     * @param doTransfer If an actual transfer should be performed
     */

    function __mint(address minter, uint256 mintAmount, bool doTransfer) private {
        IOperatorDefender(operator).beforeMTokenMint(address(this), minter);

        Exp memory exchangeRate = Exp({mantissa: _exchangeRateStored()});

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /*
         *  We call `_doTransferIn` for the minter and the mintAmount.
         *  Note: The mToken must handle variations between ERC-20 and ETH underlying.
         *  `_doTransferIn` reverts if anything goes wrong, since we can't be sure if
         *  side-effects occurred. The function returns the amount actually transferred,
         *  in case of a fee. On success, the mToken holds an additional `actualMintAmount`
         *  of cash.
         */
        uint256 actualMintAmount = doTransfer ? _doTransferIn(minter, mintAmount) : mintAmount;
        initialExchangeRateMantissa += actualMintAmount;

        /*
         * We get the current exchange rate and calculate the number of mTokens to be minted:
         *  mintTokens = actualMintAmount / exchangeRate
         */

        uint256 mintTokens = div_(actualMintAmount, exchangeRate);

        // avoid exchangeRate manipulation
        if (totalSupply == 0) {
            totalSupply = 1000;
            accountTokens[address(0)] = 1000;
            mintTokens -= 1000;
        }

        /*
         * We calculate the new total supply of mTokens and minter token balance, checking for overflow:
         *  totalSupplyNew = totalSupply + mintTokens
         *  accountTokensNew = accountTokens[minter] + mintTokens
         * And write them into storage
         */
        totalSupply = totalSupply + mintTokens;
        accountTokens[minter] = accountTokens[minter] + mintTokens;

        /* We emit a Mint event, and a Transfer event */
        emit Mint(minter, actualMintAmount, mintTokens);
        emit Transfer(address(this), minter, mintTokens);

        /* We call the defense hook */
        IOperatorDefender(operator).afterMTokenMint(address(this));
    }

    /**
     * @notice Transfer `tokens` tokens from `src` to `dst` by `spender`
     * @dev Called by both `transfer` and `transferFrom` internally
     * @param spender The address of the account performing the transfer
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param tokens The number of tokens to transfer
     */
    function _transferTokens(address spender, address src, address dst, uint256 tokens) private {
        IOperatorDefender(operator).beforeMTokenTransfer(address(this), src, dst, tokens);

        require(src != dst, mToken_TransferNotValid());

        /* Get the allowance, infinite for the account owner */
        uint256 startingAllowance = 0;
        if (spender == src) {
            startingAllowance = type(uint256).max;
        } else {
            startingAllowance = transferAllowances[src][spender];
        }

        /* Do the calculations, checking for {under,over}flow */
        uint256 allowanceNew = startingAllowance - tokens;
        uint256 srcTokensNew = accountTokens[src] - tokens;
        uint256 dstTokensNew = accountTokens[dst] + tokens;

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        accountTokens[src] = srcTokensNew;
        accountTokens[dst] = dstTokensNew;

        /* Eat some of the allowance (if necessary) */
        if (startingAllowance != type(uint256).max) {
            transferAllowances[src][spender] = allowanceNew;
        }

        /* We emit a Transfer event */
        emit Transfer(src, dst, tokens);
    }
}

File 20 of 25 : IInterestRateModel.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

/**
 * @title IInterestRateModel
 * @notice Interface for the interest rate contracts
 */
interface IInterestRateModel {
    /// @notice Emitted when interest rate parameters are updated
    /// @param baseRatePerBlock The base rate per block
    /// @param multiplierPerBlock The multiplier per block for the interest rate slope
    /// @param jumpMultiplierPerBlock The multiplier after hitting the kink
    /// @param kink The utilization point where the jump multiplier is applied
    event NewInterestParams(
        uint256 baseRatePerBlock, uint256 multiplierPerBlock, uint256 jumpMultiplierPerBlock, uint256 kink
    );

    /**
     * @notice Should return true
     */
    function isInterestRateModel() external view returns (bool);

    /**
     * @notice The approximate number of blocks per year that is assumed by the interest rate model
     * @return The number of blocks per year
     */
    function blocksPerYear() external view returns (uint256);

    /**
     * @notice The multiplier of utilization rate that gives the slope of the interest rate
     * @return The multiplier per block
     */
    function multiplierPerBlock() external view returns (uint256);

    /**
     * @notice The base interest rate which is the y-intercept when utilization rate is 0
     * @return The base rate per block
     */
    function baseRatePerBlock() external view returns (uint256);

    /**
     * @notice The multiplierPerBlock after hitting a specified utilization point
     * @return The jump multiplier per block
     */
    function jumpMultiplierPerBlock() external view returns (uint256);

    /**
     * @notice The utilization point at which the jump multiplier is applied
     * @return The utilization point (kink)
     */
    function kink() external view returns (uint256);

    /**
     * @notice A name for user-friendliness, e.g. WBTC
     * @return The name of the interest rate model
     */
    function name() external view returns (string memory);

    /**
     * @notice Calculates the utilization rate of the market
     * @param cash The total cash in the market
     * @param borrows The total borrows in the market
     * @param reserves The total reserves in the market
     * @return The utilization rate as a mantissa between [0, 1e18]
     */
    function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) external pure returns (uint256);

    /**
     * @notice Returns the current borrow rate per block for the market
     * @param cash The total cash in the market
     * @param borrows The total borrows in the market
     * @param reserves The total reserves in the market
     * @return The current borrow rate per block, scaled by 1e18
     */
    function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view returns (uint256);

    /**
     * @notice Returns the current supply rate per block for the market
     * @param cash The total cash in the market
     * @param borrows The total borrows in the market
     * @param reserves The total reserves in the market
     * @param reserveFactorMantissa The current reserve factor for the market
     * @return The current supply rate per block, scaled by 1e18
     */
    function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa)
        external
        view
        returns (uint256);
}

File 21 of 25 : IOperator.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

import {IRoles} from "./IRoles.sol";
import {ImTokenOperationTypes} from "./ImToken.sol";

interface IOperatorData {
    struct Market {
        // Whether or not this market is listed
        bool isListed;
        //  Multiplier representing the most one can borrow against their collateral in this market.
        //  For instance, 0.9 to allow borrowing 90% of collateral value.
        //  Must be between 0 and 1, and stored as a mantissa.
        uint256 collateralFactorMantissa;
        // Per-market mapping of "accounts in this asset"
        mapping(address => bool) accountMembership;
        // Whether or not this market receives MALDA
        bool isMalded;
    }
}

interface IOperatorDefender {
    /**
     * @notice Checks if the account should be allowed to transfer tokens in the given market
     * @param mToken The market to verify the transfer against
     * @param src The account which sources the tokens
     * @param dst The account which receives the tokens
     * @param transferTokens The number of mTokens to transfer
     */
    function beforeMTokenTransfer(address mToken, address src, address dst, uint256 transferTokens) external;

    /**
     * @notice Checks if the account should be allowed to mint tokens in the given market
     * @param mToken The market to verify the mint against
     * @param minter The account which would get the minted tokens
     */
    function beforeMTokenMint(address mToken, address minter) external;

    /**
     * @notice Validates mint and reverts on rejection. May emit logs.
     * @param mToken Asset being minted
     */
    function afterMTokenMint(address mToken) external view;

    /**
     * @notice Checks if the account should be allowed to redeem tokens in the given market
     * @param mToken The market to verify the redeem against
     * @param redeemer The account which would redeem the tokens
     * @param redeemTokens The number of mTokens to exchange for the underlying asset in the market
     */
    function beforeMTokenRedeem(address mToken, address redeemer, uint256 redeemTokens) external;

    /**
     * @notice Checks if the account should be allowed to borrow the underlying asset of the given market
     * @param mToken The market to verify the borrow against
     * @param borrower The account which would borrow the asset
     * @param borrowAmount The amount of underlying the account would borrow
     */
    function beforeMTokenBorrow(address mToken, address borrower, uint256 borrowAmount) external;

    /**
     * @notice Checks if the account should be allowed to repay a borrow in the given market
     * @param mToken The market to verify the repay against
     * @param borrower The account which would borrowed the asset
     */
    function beforeMTokenRepay(address mToken, address borrower) external;

    /**
     * @notice Checks if the liquidation should be allowed to occur
     * @param mTokenBorrowed Asset which was borrowed by the borrower
     * @param mTokenCollateral Asset which was used as collateral and will be seized
     * @param borrower The address of the borrower
     * @param repayAmount The amount of underlying being repaid
     */
    function beforeMTokenLiquidate(
        address mTokenBorrowed,
        address mTokenCollateral,
        address borrower,
        uint256 repayAmount
    ) external view;

    /**
     * @notice Checks if the seizing of assets should be allowed to occur
     * @param mTokenCollateral Asset which was used as collateral and will be seized
     * @param mTokenBorrowed Asset which was borrowed by the borrower
     * @param liquidator The address repaying the borrow and seizing the collateral
     * @param borrower The address of the borrower
     */
    function beforeMTokenSeize(address mTokenCollateral, address mTokenBorrowed, address liquidator, address borrower)
        external;
}

interface IOperator {
    // ----------- VIEW ------------
    /**
     * @notice Should return true
     */
    function isOperator() external view returns (bool);

    /**
     * @notice Returns if operation is paused
     * @param mToken The mToken to check
     * @param _type the operation type
     */
    function isPaused(address mToken, ImTokenOperationTypes.OperationType _type) external view returns (bool);

    /**
     * @notice Roles manager
     */
    function rolesOperator() external view returns (IRoles);

    /**
     * @notice Oracle which gives the price of any given asset
     */
    function oracleOperator() external view returns (address);

    /**
     * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
     */
    function closeFactorMantissa() external view returns (uint256);

    /**
     * @notice Multiplier representing the discount on collateral that a liquidator receives
     */
    function liquidationIncentiveMantissa() external view returns (uint256);

    /**
     * @notice Returns the assets an account has entered
     * @param _user The address of the account to pull assets for
     * @return mTokens A dynamic list with the assets the account has entered
     */
    function getAssetsIn(address _user) external view returns (address[] memory mTokens);

    /**
     * @notice A list of all markets
     */
    function getAllMarkets() external view returns (address[] memory mTokens);

    /**
     * @notice Borrow caps enforced by borrowAllowed for each mToken address. Defaults to zero which corresponds to unlimited borrowing.
     */
    function borrowCaps(address _mToken) external view returns (uint256);

    /**
     * @notice Supply caps enforced by supplyAllowed for each mToken address. Defaults to zero which corresponds to unlimited supplying.
     */
    function supplyCaps(address _mToken) external view returns (uint256);

    /**
     * @notice Reward Distributor to markets supply and borrow (including protocol token)
     */
    function rewardDistributor() external view returns (address);

    /**
     * @notice Returns whether the given account is entered in the given asset
     * @param account The address of the account to check
     * @param mToken The mToken to check
     * @return True if the account is in the asset, otherwise false.
     */
    function checkMembership(address account, address mToken) external view returns (bool);

    /**
     * @notice Determine the current account liquidity wrt collateral requirements
     * @return  account liquidity in excess of collateral requirements,
     *          account shortfall below collateral requirements)
     */
    function getAccountLiquidity(address account) external view returns (uint256, uint256);

    /**
     * @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
     * @param mTokenModify The market to hypothetically redeem/borrow in
     * @param account The account to determine liquidity for
     * @param redeemTokens The number of tokens to hypothetically redeem
     * @param borrowAmount The amount of underlying to hypothetically borrow
     * @return hypothetical account liquidity in excess of collateral requirements,
     *         hypothetical account shortfall below collateral requirements)
     */
    function getHypotheticalAccountLiquidity(
        address account,
        address mTokenModify,
        uint256 redeemTokens,
        uint256 borrowAmount
    ) external view returns (uint256, uint256);

    /**
     * @notice Calculate number of tokens of collateral asset to seize given an underlying amount
     * @dev Used in liquidation (called in mTokenBorrowed.liquidate)
     * @param mTokenBorrowed The address of the borrowed mToken
     * @param mTokenCollateral The address of the collateral mToken
     * @param actualRepayAmount The amount of mTokenBorrowed underlying to convert into mTokenCollateral tokens
     * @return number of mTokenCollateral tokens to be seized in a liquidation
     */
    function liquidateCalculateSeizeTokens(address mTokenBorrowed, address mTokenCollateral, uint256 actualRepayAmount)
        external
        view
        returns (uint256);

    /**
     * @notice Returns true if the given mToken market has been deprecated
     * @dev All borrows in a deprecated mToken market can be immediately liquidated
     * @param mToken The market to check if deprecated
     */
    function isDeprecated(address mToken) external view returns (bool);

    // ----------- ACTIONS ------------
    /**
     * @notice Set pause for a specific operation
     * @param mToken The market token address
     * @param _type The pause operation type
     * @param state The pause operation status
     */
    function setPaused(address mToken, ImTokenOperationTypes.OperationType _type, bool state) external;

    /**
     * @notice Add assets to be included in account liquidity calculation
     * @param _mTokens The list of addresses of the mToken markets to be enabled
     */
    function enterMarkets(address[] calldata _mTokens) external;

    /**
     * @notice Removes asset from sender's account liquidity calculation
     * @dev Sender must not have an outstanding borrow balance in the asset,
     *  or be providing necessary collateral for an outstanding borrow.
     * @param _mToken The address of the asset to be removed
     */
    function exitMarket(address _mToken) external;

    /**
     * @notice Claim all the MALDA accrued by holder in all markets
     * @param holder The address to claim MALDA for
     */
    function claimMalda(address holder) external;

    /**
     * @notice Claim all the MALDA accrued by holder in the specified markets
     * @param holder The address to claim MALDA for
     * @param mTokens The list of markets to claim MALDA in
     */
    function claimMalda(address holder, address[] memory mTokens) external;

    /**
     * @notice Claim all MALDA accrued by the holders
     * @param holders The addresses to claim MALDA for
     * @param mTokens The list of markets to claim MALDA in
     * @param borrowers Whether or not to claim MALDA earned by borrowing
     * @param suppliers Whether or not to claim MALDA earned by supplying
     */
    function claimMalda(address[] memory holders, address[] memory mTokens, bool borrowers, bool suppliers) external;
}

File 22 of 25 : mTokenConfiguration.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {IOperator} from "src/interfaces/IOperator.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";

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

abstract contract mTokenConfiguration is mTokenStorage {
    // ----------- MODIFIERS ------------
    modifier onlyAdmin() {
        require(msg.sender == admin, mToken_OnlyAdmin());
        _;
    }

    // ----------- OWNER ------------
    /**
     * @notice Sets a new Operator for the market
     * @dev Admin function to set a new operator
     */
    function setOperator(address _operator) external onlyAdmin {
        _setOperator(_operator);
    }

    /**
     * @notice Sets a new Operator for the market
     * @dev Admin function to set a new operator
     */
    function setRolesOperator(address _roles) external onlyAdmin {
        require(_roles != address(0), mToken_InvalidInput());

        emit NewRolesOperator(address(rolesOperator), _roles);

        rolesOperator = IRoles(_roles);
    }

    /**
     * @notice accrues interest and updates the interest rate model using _setInterestRateModelFresh
     * @dev Admin function to accrue interest and update the interest rate model
     * @param newInterestRateModel the new interest rate model to use
     */
    function setInterestRateModel(address newInterestRateModel) external onlyAdmin {
        _accrueInterest();
        // emits interest-rate-model-update-specific logs on errors, so we don't need to.
        return _setInterestRateModel(newInterestRateModel);
    }

    function setBorrowRateMaxMantissa(uint256 maxMantissa) external onlyAdmin {
        uint256 _oldVal = borrowRateMaxMantissa;
        borrowRateMaxMantissa = maxMantissa;

        // validate new mantissa
        _accrueInterest();

        emit NewBorrowRateMaxMantissa(_oldVal, maxMantissa);
    }

    /**
     * @notice accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh
     * @dev Admin function to accrue interest and set a new reserve factor
     */
    function setReserveFactor(uint256 newReserveFactorMantissa) external onlyAdmin {
        _accrueInterest();

        require(newReserveFactorMantissa <= RESERVE_FACTOR_MAX_MANTISSA, mToken_ReserveFactorTooHigh());

        emit NewReserveFactor(reserveFactorMantissa, newReserveFactorMantissa);
        reserveFactorMantissa = newReserveFactorMantissa;
    }

    /**
     * @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
     * @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
     * @param newPendingAdmin New pending admin.
     */
    function setPendingAdmin(address payable newPendingAdmin) external onlyAdmin {
        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(pendingAdmin, newPendingAdmin);

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;
    }

    /**
     * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
     * @dev Admin function for pending admin to accept role and update admin
     */
    function acceptAdmin() external {
        // Check caller is pendingAdmin
        require(msg.sender == pendingAdmin, mToken_OnlyAdmin());

        // Save current values for inclusion in log
        address oldAdmin = admin;
        address oldPendingAdmin = pendingAdmin;

        // Store admin with value pendingAdmin
        admin = pendingAdmin;

        // Clear the pending value
        pendingAdmin = payable(address(0));

        emit NewAdmin(oldAdmin, admin);
        emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
    }

    // ----------- INTERNAL ------------
    /**
     * @notice updates the interest rate model (*requires fresh interest accrual)
     * @dev Admin function to update the interest rate model
     * @param newInterestRateModel the new interest rate model to use
     */
    function _setInterestRateModel(address newInterestRateModel) internal onlyAdmin {
        // Ensure invoke newInterestRateModel.isInterestRateModel() returns true
        require(IInterestRateModel(newInterestRateModel).isInterestRateModel(), mToken_MarketMethodNotValid());

        emit NewMarketInterestRateModel(interestRateModel, newInterestRateModel);
        interestRateModel = newInterestRateModel;
    }

    function _setOperator(address _operator) internal {
        require(IOperator(_operator).isOperator(), mToken_MarketMethodNotValid());

        emit NewOperator(operator, _operator);

        operator = _operator;
    }
}

File 23 of 25 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}

File 24 of 25 : mTokenStorage.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

// interfaces
import {IRoles} from "src/interfaces/IRoles.sol";
import {ImToken, ImTokenMinimal} from "src/interfaces/ImToken.sol";
import {IInterestRateModel} from "src/interfaces/IInterestRateModel.sol";

// contracts
import {ExponentialNoError} from "src/utils/ExponentialNoError.sol";

abstract contract mTokenStorage is ImToken, ExponentialNoError {
    // ----------- ACCESS STORAGE ------------
    /**
     * @inheritdoc ImToken
     */
    address payable public admin;

    /**
     * @inheritdoc ImToken
     */
    address payable public pendingAdmin;

    /**
     * @inheritdoc ImToken
     */
    address public operator;

    /**
     * @inheritdoc ImToken
     */
    IRoles public rolesOperator;

    // ----------- TOKENS STORAGE ------------
    /**
     * @inheritdoc ImTokenMinimal
     */
    string public name;

    /**
     * @inheritdoc ImTokenMinimal
     */
    string public symbol;

    /**
     * @inheritdoc ImTokenMinimal
     */
    uint8 public decimals;

    // ----------- MARKET STORAGE ------------
    /**
     * @inheritdoc ImToken
     */
    address public interestRateModel;

    /**
     * @inheritdoc ImToken
     */
    uint256 public reserveFactorMantissa;

    /**
     * @inheritdoc ImToken
     */
    uint256 public accrualBlockNumber;

    /**
     * @inheritdoc ImToken
     */
    uint256 public borrowIndex;

    /**
     * @inheritdoc ImToken
     */
    uint256 public totalBorrows;

    /**
     * @inheritdoc ImToken
     */
    uint256 public totalReserves;

    /**
     * @inheritdoc ImTokenMinimal
     */
    uint256 public totalSupply;

    /**
     * @notice Maximum borrow rate that can ever be applied (.0005% / block)
     */
    uint256 public borrowRateMaxMantissa = 0.00004e16;

    /**
     * @notice Container for borrow balance information
     * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
     * @member interestIndex Global borrowIndex as of the most recent balance-changing action
     */
    struct BorrowSnapshot {
        uint256 principal;
        uint256 interestIndex;
    }

    // Mapping of account addresses to outstanding borrow balances
    mapping(address => BorrowSnapshot) internal accountBorrows;

    // Official record of token balances for each account
    mapping(address => uint256) internal accountTokens;

    // Approved token transfer amounts on behalf of others
    mapping(address => mapping(address => uint256)) internal transferAllowances;

    /**
     * @notice Initial exchange rate used when minting the first mTokens (used when totalSupply = 0)
     */
    uint256 internal initialExchangeRateMantissa;

    /**
     * @notice Maximum fraction of interest that can be set aside for reserves
     */
    uint256 internal constant RESERVE_FACTOR_MAX_MANTISSA = 1e18;

    /**
     * @notice Share of seized collateral that is added to reserves
     */
    uint256 internal constant PROTOCOL_SEIZE_SHARE_MANTISSA = 2.8e16; //2.8%

    // ----------- ERRORS ------------
    error mToken_OnlyAdmin();
    error mToken_RedeemEmpty();
    error mToken_InvalidInput();
    error mToken_OnlyAdminOrRole();
    error mToken_TransferNotValid();
    error mToken_BorrowRateTooHigh();
    error mToken_AlreadyInitialized();
    error mToken_ReserveFactorTooHigh();
    error mToken_BlockNumberNotValid();
    error mToken_ExchangeRateNotValid();
    error mToken_MarketMethodNotValid();
    error mToken_LiquidateSeizeTooMuch();
    error mToken_RedeemCashNotAvailable();
    error mToken_BorrowCashNotAvailable();
    error mToken_ReserveCashNotAvailable();
    error mToken_RedeemTransferOutNotPossible();
    error mToken_CollateralBlockNumberNotValid();

    // ----------- ACCESS EVENTS ------------
    /**
     * @notice Event emitted when rolesOperator is changed
     */
    event NewRolesOperator(address indexed oldRoles, address indexed newRoles);

    /**
     * @notice Event emitted when pendingAdmin is changed
     */
    event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);

    /**
     * @notice Event emitted when pendingAdmin is accepted, which means admin is updated
     */
    event NewAdmin(address indexed oldAdmin, address indexed newAdmin);

    /**
     * @notice Event emitted when Operator is changed
     */
    event NewOperator(address indexed oldOperator, address indexed newOperator);

    // ----------- TOKENS EVENTS ------------
    /**
     * @notice EIP20 Transfer event
     */
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /**
     * @notice EIP20 Approval event
     */
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    // ----------- MARKETS EVENTS ------------
    /**
     * @notice Event emitted when interest is accrued
     */
    event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);

    /**
     * @notice Event emitted when tokens are minted
     */
    event Mint(address indexed minter, uint256 mintAmount, uint256 mintTokens);

    /**
     * @notice Event emitted when tokens are redeemed
     */
    event Redeem(address indexed redeemer, uint256 redeemAmount, uint256 redeemTokens);

    /**
     * @notice Event emitted when underlying is borrowed
     */
    event Borrow(address indexed borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);

    /**
     * @notice Event emitted when a borrow is repaid
     */
    event RepayBorrow(
        address indexed payer,
        address indexed borrower,
        uint256 repayAmount,
        uint256 accountBorrows,
        uint256 totalBorrows
    );

    /**
     * @notice Event emitted when a borrow is liquidated
     */
    event LiquidateBorrow(
        address indexed liquidator,
        address indexed borrower,
        uint256 repayAmount,
        address indexed mTokenCollateral,
        uint256 seizeTokens
    );

    /**
     * @notice Event emitted when interestRateModel is changed
     */
    event NewMarketInterestRateModel(address indexed oldInterestRateModel, address indexed newInterestRateModel);

    /**
     * @notice Event emitted when the reserve factor is changed
     */
    event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);

    /**
     * @notice Event emitted when the reserves are added
     */
    event ReservesAdded(address indexed benefactor, uint256 addAmount, uint256 newTotalReserves);

    /**
     * @notice Event emitted when the reserves are reduced
     */
    event ReservesReduced(address indexed admin, uint256 reduceAmount, uint256 newTotalReserves);

    /**
     * @notice Event emitted when the borrow max mantissa is updated
     */
    event NewBorrowRateMaxMantissa(uint256 oldVal, uint256 maxMantissa);

    // ----------- VIRTUAL ------------
    /**
     * @inheritdoc ImToken
     */
    function accrueInterest() external virtual {
        _accrueInterest();
    }

    /**
     * @dev Function to simply retrieve block number
     *  This exists mainly for inheriting test contracts to stub this result.
     */
    function _getBlockNumber() internal view virtual returns (uint256) {
        return block.timestamp;
    }

    /**
     * @notice Calculates the exchange rate from the underlying to the MToken
     * @dev This function does not accrue interest before calculating the exchange rate
     *      Can generate issues if inflated by an attacker when market is created
     *      Solution: use 0 collateral factor initially
     * @return calculated exchange rate scaled by 1e18
     */
    function _exchangeRateStored() internal view virtual returns (uint256) {
        uint256 _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            /*
             * If there are no tokens minted:
             *  exchangeRate = initialExchangeRate
             */
            return initialExchangeRateMantissa;
        } else {
            /*
             * Otherwise:
             *  exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
             */
            uint256 totalCash = _getCashPrior();
            uint256 cashPlusBorrowsMinusReserves = totalCash + totalBorrows - totalReserves;
            uint256 exchangeRate = (cashPlusBorrowsMinusReserves * expScale) / _totalSupply;

            return exchangeRate;
        }
    }

    /**
     * @notice Gets balance of this contract in terms of the underlying
     * @dev This excludes the value of the current message, if any
     * @return The quantity of underlying owned by this contract
     */
    function _getCashPrior() internal view virtual returns (uint256);

    /**
     * @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
     *  This may revert due to insufficient balance or insufficient allowance.
     */
    function _doTransferIn(address from, uint256 amount) internal virtual returns (uint256);

    /**
     * @dev Performs a transfer out, ideally returning an explanatory error code upon failure rather than reverting.
     *  If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
     *  If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
     */
    function _doTransferOut(address payable to, uint256 amount) internal virtual;

    // ----------- NON-VIRTUAL ------------
    function _accrueInterest() internal {
        /* Remember the initial block number */
        uint256 currentBlockNumber = _getBlockNumber();
        uint256 accrualBlockNumberPrior = accrualBlockNumber;

        /* Short-circuit accumulating 0 interest */
        if (accrualBlockNumberPrior == currentBlockNumber) return;

        /* Read the previous values out of storage */
        uint256 cashPrior = _getCashPrior();
        uint256 borrowsPrior = totalBorrows;
        uint256 reservesPrior = totalReserves;
        uint256 borrowIndexPrior = borrowIndex;

        /* Calculate the current borrow interest rate */
        uint256 borrowRateMantissa =
            IInterestRateModel(interestRateModel).getBorrowRate(cashPrior, borrowsPrior, reservesPrior);
        require(borrowRateMantissa <= borrowRateMaxMantissa, mToken_BorrowRateTooHigh());

        /* Calculate the number of blocks elapsed since the last accrual */
        uint256 blockDelta = currentBlockNumber - accrualBlockNumberPrior;

        /*
         * Calculate the interest accumulated into borrows and reserves and the new index:
         *  simpleInterestFactor = borrowRate * blockDelta
         *  interestAccumulated = simpleInterestFactor * totalBorrows
         *  totalBorrowsNew = interestAccumulated + totalBorrows
         *  totalReservesNew = interestAccumulated * reserveFactor + totalReserves
         *  borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
         */

        Exp memory simpleInterestFactor = mul_(Exp({mantissa: borrowRateMantissa}), blockDelta);
        uint256 interestAccumulated = mul_ScalarTruncate(simpleInterestFactor, borrowsPrior);
        uint256 totalBorrowsNew = interestAccumulated + borrowsPrior;
        uint256 totalReservesNew =
            mul_ScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior);
        uint256 borrowIndexNew = mul_ScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);

        /////////////////////////
        // EFFECTS & INTERACTIONS
        // (No safe failures beyond this point)

        /* We write the previously calculated values into storage */
        accrualBlockNumber = currentBlockNumber;
        borrowIndex = borrowIndexNew;
        totalBorrows = totalBorrowsNew;
        totalReserves = totalReservesNew;

        /* We emit an AccrueInterest event */
        emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);
    }
}

File 25 of 25 : ExponentialNoError.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.28;

/*
 _____ _____ __    ____  _____ 
|     |  _  |  |  |    \|  _  |
| | | |     |  |__|  |  |     |
|_|_|_|__|__|_____|____/|__|__|   
*/

/**
 * @title Exponential module for storing fixed-precision decimals
 * @author Compound
 * @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
 *         Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
 *         `Exp({mantissa: 5100000000000000000})`.
 */
abstract contract ExponentialNoError {
    uint256 constant expScale = 1e18;
    uint256 constant doubleScale = 1e36;
    uint256 constant halfExpScale = expScale / 2;
    uint256 constant mantissaOne = expScale;

    struct Exp {
        uint256 mantissa;
    }

    struct Double {
        uint256 mantissa;
    }

    /**
     * @dev Truncates the given exp to a whole number value.
     *      For example, truncate(Exp{mantissa: 15 * expScale}) = 15
     */
    function truncate(Exp memory exp) internal pure returns (uint256) {
        // Note: We are not using careful math here as we're performing a division that cannot fail
        return exp.mantissa / expScale;
    }

    /**
     * @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
     */
    function mul_ScalarTruncate(Exp memory a, uint256 scalar) internal pure returns (uint256) {
        Exp memory product = mul_(a, scalar);
        return truncate(product);
    }

    /**
     * @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
     */
    function mul_ScalarTruncateAddUInt(Exp memory a, uint256 scalar, uint256 addend) internal pure returns (uint256) {
        Exp memory product = mul_(a, scalar);
        return add_(truncate(product), addend);
    }

    /**
     * @dev Checks if first Exp is less than second Exp.
     */
    function lessThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {
        return left.mantissa < right.mantissa;
    }

    /**
     * @dev Checks if left Exp <= right Exp.
     */
    function lessThanOrEqualExp(Exp memory left, Exp memory right) internal pure returns (bool) {
        return left.mantissa <= right.mantissa;
    }

    /**
     * @dev Checks if left Exp > right Exp.
     */
    function greaterThanExp(Exp memory left, Exp memory right) internal pure returns (bool) {
        return left.mantissa > right.mantissa;
    }

    /**
     * @dev returns true if Exp is exactly zero
     */
    function isZeroExp(Exp memory value) internal pure returns (bool) {
        return value.mantissa == 0;
    }

    function safe224(uint256 n, string memory errorMessage) internal pure returns (uint224) {
        require(n < 2 ** 224, errorMessage);
        return uint224(n);
    }

    function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2 ** 32, errorMessage);
        return uint32(n);
    }

    function add_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
        return Exp({mantissa: add_(a.mantissa, b.mantissa)});
    }

    function add_(Double memory a, Double memory b) internal pure returns (Double memory) {
        return Double({mantissa: add_(a.mantissa, b.mantissa)});
    }

    function add_(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    function sub_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
        return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
    }

    function sub_(Double memory a, Double memory b) internal pure returns (Double memory) {
        return Double({mantissa: sub_(a.mantissa, b.mantissa)});
    }

    function sub_(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    function mul_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
        return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
    }

    function mul_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
        return Exp({mantissa: mul_(a.mantissa, b)});
    }

    function mul_(uint256 a, Exp memory b) internal pure returns (uint256) {
        return mul_(a, b.mantissa) / expScale;
    }

    function mul_(Double memory a, Double memory b) internal pure returns (Double memory) {
        return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
    }

    function mul_(Double memory a, uint256 b) internal pure returns (Double memory) {
        return Double({mantissa: mul_(a.mantissa, b)});
    }

    function mul_(uint256 a, Double memory b) internal pure returns (uint256) {
        return mul_(a, b.mantissa) / doubleScale;
    }

    function mul_(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    function div_(Exp memory a, Exp memory b) internal pure returns (Exp memory) {
        return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
    }

    function div_(Exp memory a, uint256 b) internal pure returns (Exp memory) {
        return Exp({mantissa: div_(a.mantissa, b)});
    }

    function div_(uint256 a, Exp memory b) internal pure returns (uint256) {
        return div_(mul_(a, expScale), b.mantissa);
    }

    function div_(Double memory a, Double memory b) internal pure returns (Double memory) {
        return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
    }

    function div_(Double memory a, uint256 b) internal pure returns (Double memory) {
        return Double({mantissa: div_(a.mantissa, b)});
    }

    function div_(uint256 a, Double memory b) internal pure returns (uint256) {
        return div_(mul_(a, doubleScale), b.mantissa);
    }

    function div_(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    function fraction(uint256 a, uint256 b) internal pure returns (Double memory) {
        return Double({mantissa: div_(mul_(a, doubleScale), b)});
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "risc0/=lib/risc0-ethereum/contracts/src/",
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "risc0-ethereum/=lib/risc0-ethereum/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 2499
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": false,
  "libraries": {}
}

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZkVerifier_ImageNotValid","type":"error"},{"inputs":[],"name":"ZkVerifier_InputNotValid","type":"error"},{"inputs":[],"name":"ZkVerifier_VerifierNotSet","type":"error"},{"inputs":[],"name":"mErc20Host_AddressNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_AmountNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_AmountTooBig","type":"error"},{"inputs":[],"name":"mErc20Host_CallerNotAllowed","type":"error"},{"inputs":[],"name":"mErc20Host_ChainNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_DstChainNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_JournalNotValid","type":"error"},{"inputs":[],"name":"mErc20Host_LengthMismatch","type":"error"},{"inputs":[],"name":"mErc20Host_NotRebalancer","type":"error"},{"inputs":[],"name":"mErc20Host_ProofGenerationInputNotValid","type":"error"},{"inputs":[],"name":"mErc20_TokenNotValid","type":"error"},{"inputs":[],"name":"mToken_AlreadyInitialized","type":"error"},{"inputs":[],"name":"mToken_BlockNumberNotValid","type":"error"},{"inputs":[],"name":"mToken_BorrowCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_BorrowRateTooHigh","type":"error"},{"inputs":[],"name":"mToken_CollateralBlockNumberNotValid","type":"error"},{"inputs":[],"name":"mToken_ExchangeRateNotValid","type":"error"},{"inputs":[],"name":"mToken_InvalidInput","type":"error"},{"inputs":[],"name":"mToken_LiquidateSeizeTooMuch","type":"error"},{"inputs":[],"name":"mToken_MarketMethodNotValid","type":"error"},{"inputs":[],"name":"mToken_OnlyAdmin","type":"error"},{"inputs":[],"name":"mToken_OnlyAdminOrRole","type":"error"},{"inputs":[],"name":"mToken_RedeemCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_RedeemEmpty","type":"error"},{"inputs":[],"name":"mToken_RedeemTransferOutNotPossible","type":"error"},{"inputs":[],"name":"mToken_ReserveCashNotAvailable","type":"error"},{"inputs":[],"name":"mToken_ReserveFactorTooHigh","type":"error"},{"inputs":[],"name":"mToken_TransferNotValid","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cashPrior","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAccumulated","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AllowedCallerUpdated","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":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_imageId","type":"bytes32"}],"name":"ImageSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":true,"internalType":"address","name":"mTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintTokens","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldVal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxMantissa","type":"uint256"}],"name":"NewBorrowRateMaxMantissa","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldInterestRateModel","type":"address"},{"indexed":true,"internalType":"address","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOperator","type":"address"},{"indexed":true,"internalType":"address","name":"newOperator","type":"address"}],"name":"NewOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactorMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldRoles","type":"address"},{"indexed":true,"internalType":"address","name":"newRoles","type":"address"}],"name":"NewRolesOperator","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"redeemer","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payer","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"accountBorrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalBorrows","type":"uint256"}],"name":"RepayBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"benefactor","type":"address"},{"indexed":false,"internalType":"uint256","name":"addAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"uint256","name":"reduceAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesReduced","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":"oldVerifier","type":"address"},{"indexed":true,"internalType":"address","name":"newVerifier","type":"address"}],"name":"VerifierSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_BorrowExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_BorrowOnExternsionChain","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"mErc20Host_ChainStatusUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":false,"internalType":"address","name":"userToLiquidate","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint32","name":"srcChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_LiquidateExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_MintExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"address","name":"position","type":"address"},{"indexed":false,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_RepayExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"msgSender","type":"address"},{"indexed":true,"internalType":"address","name":"srcSender","type":"address"},{"indexed":true,"internalType":"uint32","name":"chainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_WithdrawExternal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint32","name":"dstChainId","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mErc20Host_WithdrawOnExtensionChain","type":"event"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"accAmountInPerChain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"address","name":"","type":"address"}],"name":"accAmountOutPerChain","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"addReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"admin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowedCallers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"allowedChains","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"borrowOnExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowRateMaxMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegateMaldaLikeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"extractForRebalancing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountSnapshot","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint32","name":"dstId","type":"uint32"}],"name":"getProofData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"imageId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"},{"internalType":"address","name":"zkVerifier_","type":"address"},{"internalType":"address","name":"roles_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_verifier","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"caller","type":"address"}],"name":"isCallerAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"address","name":"mTokenCollateral","type":"address"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"address[]","name":"userToLiquidate","type":"address[]"},{"internalType":"uint256[]","name":"liquidateAmount","type":"uint256[]"},{"internalType":"address[]","name":"collateral","type":"address[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"liquidateExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"uint256[]","name":"mintAmount","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mintExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"address","name":"operator_","type":"address"},{"internalType":"address","name":"interestRateModel_","type":"address"},{"internalType":"uint256","name":"initialExchangeRateMantissa_","type":"uint256"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address payable","name":"admin_","type":"address"}],"name":"proxyInitialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"reduceReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBehalf","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"journalData","type":"bytes"},{"internalType":"bytes","name":"seal","type":"bytes"},{"internalType":"uint256[]","name":"repayAmount","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"repayExternal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rolesOperator","outputs":[{"internalType":"contract IRoles","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"liquidator","type":"address"},{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"seize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxMantissa","type":"uint256"}],"name":"setBorrowRateMaxMantissa","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_imageId","type":"bytes32"}],"name":"setImageId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newInterestRateModel","type":"address"}],"name":"setInterestRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"setPendingAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"setReserveFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_roles","type":"address"}],"name":"setRolesOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setTotalUnderlying","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_risc0Verifier","type":"address"}],"name":"setVerifier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrowsCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"updateAllowedCallerStatus","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_chainId","type":"uint32"},{"internalType":"bool","name":"_status","type":"bool"}],"name":"updateAllowedChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"verifier","outputs":[{"internalType":"contract IRiscZeroVerifier","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"dstChainId","type":"uint32"}],"name":"withdrawOnExtension","outputs":[],"stateMutability":"nonpayable","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106105145760003560e01c80637f7b13d4116102a1578063b2a02ff11161016b578063dd62ed3e116100e3578063f851a44011610097578063f8f9da281161007c578063f8f9da2814610ad6578063fe62355f14610ade578063ffcaadfe14610b1a57600080fd5b8063f851a44014610ab0578063f89416ee14610ac357600080fd5b8063ee27a2f2116100c8578063ee27a2f214610a86578063ef3f7dd514610a8f578063f3fdb15a14610a9857600080fd5b8063dd62ed3e14610a3a578063e67218cd14610a7357600080fd5b8063c37f68e21161013a578063c5ebeaec1161011f578063c5ebeaec14610a01578063d87f7cad14610a14578063db006a7514610a2757600080fd5b8063c37f68e2146109c0578063c4d66de8146109ee57600080fd5b8063b2a02ff11461097f578063b3ab15fb14610992578063b57a4a72146109a5578063bd6d894d146109b857600080fd5b806399c4383711610219578063a66b92ab116101cd578063a9059cbb116101b2578063a9059cbb1461095b578063aa5af0fd1461096e578063ae9d70b01461097757600080fd5b8063a66b92ab14610940578063a6afed951461095357600080fd5b80639d9339b3116101fe5780639d9339b314610907578063a0712d681461091a578063a4777a7a1461092d57600080fd5b806399c43837146108e157806399d8c1b4146108f457600080fd5b80638f840ddd1161027057806391a0d4381161025557806391a0d438146108b357806395d89b41146108c657806395dd9193146108ce57600080fd5b80638f840ddd14610897578063901129c2146108a057600080fd5b80637f7b13d41461083b578063852a12e31461084e5780638bcd4016146108615780638da735271461087457600080fd5b80633936c445116103e25780635437988d1161035a5780636c540baf1161030e57806370a08231116102f357806370a08231146107f757806373acee98146108205780637821a5141461082857600080fd5b80636c540baf146107db5780636f307dc3146107e457600080fd5b80635bdcecb71161033f5780635bdcecb714610793578063600bb376146107a6578063699cd5e2146107d457600080fd5b80635437988d1461076d578063570ca7351461078057600080fd5b80634914c008116103b15780634f2be4ce116103965780634f2be4ce1461071c5780634fbdc6ae1461072f5780634fecab701461075a57600080fd5b80634914c008146106f65780634dd18bf51461070957600080fd5b80633936c445146106a75780633af9e669146106d25780633b1d21a2146106e557806347bd3718146106ed57600080fd5b8063182df0f51161049057806326782247116104445780632b7ac3f3116104295780632b7ac3f314610662578063313ce56714610675578063371fd8e61461069457600080fd5b80632678224714610624578063277cd86a1461064f57600080fd5b80631be19560116104755780631be19560146105eb5780631c446983146105fe57806323b872dd1461061157600080fd5b8063182df0f5146105d05780631a31d465146105d857600080fd5b8063095ea7b3116104e7578063173b9904116104cc578063173b99041461059d57806317bfdfbc146105b457806318160ddd146105c757600080fd5b8063095ea7b3146105725780630e18b6811461059557600080fd5b806306fdde031461051957806307d923e91461053757806307e279591461054a57806308fee2631461055f575b600080fd5b610521610b2d565b60405161052e919061512e565b60405180910390f35b610521610545366004615175565b610bbb565b61055d6105583660046151aa565b610c7e565b005b61055d61056d366004615251565b610ee1565b61058561058036600461530b565b611063565b604051901515815260200161052e565b61055d6110d1565b6105a660075481565b60405190815260200161052e565b6105a66105c2366004615337565b6111a0565b6105a6600c5481565b6105a66111cc565b61055d6105e6366004615428565b6111db565b61055d6105f9366004615337565b611296565b61055d61060c3660046151aa565b611395565b61058561061f3660046154e3565b61144b565b600154610637906001600160a01b031681565b6040516001600160a01b03909116815260200161052e565b61055d61065d366004615524565b611475565b601454610637906001600160a01b031681565b6006546106829060ff1681565b60405160ff909116815260200161052e565b61055d6106a23660046151aa565b61151e565b6105a66106b5366004615547565b601760209081526000928352604080842090915290825290205481565b6105a66106e0366004615337565b611529565b6105a6611568565b6105a6600a5481565b61055d61070436600461557e565b611573565b61055d610717366004615337565b611586565b61055d61072a3660046155ce565b61161a565b6105a661073d366004615547565b601660209081526000928352604080842090915290825290205481565b600354610637906001600160a01b031681565b61055d61077b366004615337565b611686565b600254610637906001600160a01b031681565b61055d6107a136600461530b565b6116ba565b6105856107b43660046155fc565b601860209081526000928352604080842090915290825290205460ff1681565b6001610585565b6105a660085481565b601354610637906001600160a01b031681565b6105a6610805366004615337565b6001600160a01b03166000908152600f602052604090205490565b6105a66116c6565b61055d6108363660046151aa565b6116e9565b61055d61084936600461561a565b6116f2565b61055d61085c3660046151aa565b611885565b61055d61086f366004615337565b611891565b610585610882366004615703565b60196020526000908152604090205460ff1681565b6105a6600b5481565b61055d6108ae3660046151aa565b6118cd565b61055d6108c13660046151aa565b611901565b610521611931565b6105a66108dc366004615337565b61193e565b61055d6108ef36600461571e565b611949565b61055d61090236600461573a565b611ad1565b61055d610915366004615251565b611bd8565b61055d6109283660046151aa565b611d4e565b61055d61093b3660046157e1565b611d5a565b61055d61094e366004615524565b611f6e565b61055d61200f565b61058561096936600461530b565b612019565b6105a660095481565b6105a661203c565b61055d61098d3660046154e3565b6120db565b61055d6109a0366004615337565b6120f9565b61055d6109b33660046158fa565b61212d565b6105a6612191565b6109d36109ce366004615337565b6121b7565b6040805193845260208401929092529082015260600161052e565b61055d6109fc366004615337565b6121f2565b61055d610a0f3660046151aa565b61238e565b61055d610a22366004615337565b61239a565b61055d610a353660046151aa565b612440565b6105a6610a483660046155fc565b6001600160a01b03918216600090815260106020908152604080832093909416825291909152205490565b61055d610a813660046151aa565b61244c565b6105a6600d5481565b6105a660155481565b6006546106379061010090046001600160a01b031681565b600054610637906001600160a01b031681565b61055d610ad1366004615337565b6124c4565b6105a661257f565b610585610aec3660046155fc565b6001600160a01b03918216600090815260186020908152604080832093909416825291909152205460ff1690565b61055d610b283660046151aa565b6125d9565b60048054610b3a906159ca565b80601f0160208091040260200160405190810160405280929190818152602001828054610b66906159ca565b8015610bb35780601f10610b8857610100808354040283529160200191610bb3565b820191906000526020600020905b815481529060010190602001808311610b9657829003601f168201915b505050505081565b63ffffffff811660008181526016602090815260408083206001600160a01b03871680855290835281842054948452601783528184209084528252918290205482517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088811b82169483019490945230841b166034820152604881019490945260688401526001600160e01b03194660e090811b8216608886015285901b16608c840152815180840360700181526090909301909152905b90505b92915050565b610c86612722565b6000546001600160a01b0316331480610d975750600354604080517fd71c72e000000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163d71c72e09160048083019260209291908290030181865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610d73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d979190615a1d565b610dcd576040517f90b0f0f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dd5612765565b80610ddf60115490565b1015610e17576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b54811115610e53576040517f72d33f5d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081600b54610e639190615a50565b600b8190559050610e74338361294a565b8160116000828254610e869190615a50565b909155505060005460408051848152602081018490526001600160a01b03909216917f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e910160405180910390a250610ede6001601255565b50565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015610f34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f589190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015610fa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc59190615a1d565b610fd557610fd587878787612961565b6000610fe387890189615a63565b8051909150838114611008576040516302e262f160e01b815260040160405180910390fd5b60005b818110156110575761104f83828151811061102857611028615b49565b602002602001015187878481811061104257611042615b49565b90506020020135866129aa565b60010161100b565b50505050505050505050565b3360008181526010602090815260408083206001600160a01b03871680855292528083208590555191929182907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110bf9087815260200190565b60405180910390a35060019392505050565b6001546001600160a01b031633146110fc5760405163303676d360e01b815260040160405180910390fd5b60008054600180546001600160a01b0380821673ffffffffffffffffffffffffffffffffffffffff19808616821787559092169092556040519190921692829184917ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc91a36001546040516001600160a01b03918216918316907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a35050565b60006111aa612722565b6111b2612765565b6111bb82612b7a565b90506111c76001601255565b919050565b60006111d6612bce565b905090565b6111e9868686868686611ad1565b6013805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038916908117909155604080517f18160ddd00000000000000000000000000000000000000000000000000000000815290516318160ddd916004808201926020929091908290030181865afa158015611268573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061128c9190615a04565b5050505050505050565b6000546001600160a01b031633146112c15760405163303676d360e01b815260040160405180910390fd5b6013546001600160a01b0390811690821603611309576040517f9e14909700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611350573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113749190615a04565b600054909150611391906001600160a01b03848116911683612c37565b5050565b6000546001600160a01b031633146113c05760405163303676d360e01b815260040160405180910390fd5b6113c8612765565b670de0b6b3a764000081111561140a576040517fea1eeb4500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60075460408051918252602082018390527faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f821460910160405180910390a1600755565b6000611455612722565b61146133858585612cab565b50600161146e6001601255565b9392505050565b600082116114965760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff81166000908152601760209081526040808320338452909152812080548492906114c6908490615b5f565b909155506114d8905033836000612edc565b6040805163ffffffff831681526020810184905233917fffb02ed8426abe4f31f431f9f4c6075fcf5916fb1ba83041a0979073b27a3d7191015b60405180910390a25050565b610ede816001612ef9565b600080604051806020016040528061153f612191565b90526001600160a01b0384166000908152600f602052604090205490915061146e908290612f20565b60006111d660115490565b611581338484846001612f38565b505050565b6000546001600160a01b031633146115b15760405163303676d360e01b815260040160405180910390fd5b6001546040516001600160a01b038084169216907fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a990600090a36001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b3360008181526018602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917fb2cc4dde7f9044ba1999f7843e2f9cd1e4ce506f8cc2e16de26ce982bf113fa6910160405180910390a35050565b6000546001600160a01b031633146116b15760405163303676d360e01b815260040160405180910390fd5b610ede81612fb2565b6113918282600161305b565b60006116d0612722565b6116d8612765565b50600a546116e66001601255565b90565b610ede81613082565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561173d5750825b905060008267ffffffffffffffff16600114801561175a5750303b155b905081158015611768575080155b1561179f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156117d357845468ff00000000000000001916680100000000000000001785555b6117e38f8f8f8f8f8f8f8f61212d565b6117ec876121f2565b600380546001600160a01b0380891673ffffffffffffffffffffffffffffffffffffffff199283161790925560008054928b1692909116919091179055831561187457845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050505050565b610ede33826001612edc565b6000546001600160a01b031633146118bc5760405163303676d360e01b815260040160405180910390fd5b6118c4612765565b610ede81613115565b6000546001600160a01b031633146118f85760405163303676d360e01b815260040160405180910390fd5b610ede81613256565b6000546001600160a01b0316331461192c5760405163303676d360e01b815260040160405180910390fd5b601155565b60058054610b3a906159ca565b6000610c7882612b7a565b6000546001600160a01b03163314801590611a5e5750600354604080517fe048241300000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c913391849163e04824139160048083019260209291908290030181865afa1580156119cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ef9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611a38573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a5c9190615a1d565b155b15611a7c5760405163271bd90b60e01b815260040160405180910390fd5b63ffffffff8216600081815260196020908152604091829020805460ff191685151590811790915591519182527f3615c8513bc6436318086c0663e19b0334871c8c175a33b9340c3ecab97137cc9101611512565b6000546001600160a01b03163314611afc5760405163303676d360e01b815260040160405180910390fd5b600854158015611b0c5750600954155b611b42576040517fc99314d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008411611b7c576040517fa08b42d000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6011849055611b8a866132c5565b42600855670de0b6b3a7640000600955611ba385613115565b6004611baf8482615bb9565b506005611bbc8382615bb9565b506006805460ff191660ff929092169190911790555050505050565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611c2b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c4f9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611c98573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbc9190615a1d565b611ccc57611ccc87878787612961565b6000611cda87890189615a63565b8051909150838114611cff576040516302e262f160e01b815260040160405180910390fd5b60005b8181101561105757611d46838281518110611d1f57611d1f615b49565b6020026020010151878784818110611d3957611d39615b49565b90506020020135866133c6565b600101611d02565b610ede33826001613588565b6003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c913391849163a1bd302d9160048083019260209291908290030181865afa158015611dad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611dd19190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015611e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e3e9190615a1d565b611e4e57611e4e8b8b8b8b612961565b6000611e5c8b8d018d615a63565b8051909150858114611e81576040516302e262f160e01b815260040160405180910390fd5b808814611ea1576040516302e262f160e01b815260040160405180910390fd5b808414611ec1576040516302e262f160e01b815260040160405180910390fd5b60005b81811015611f5e57611f56838281518110611ee157611ee1615b49565b60200260200101518b8b84818110611efb57611efb615b49565b9050602002016020810190611f109190615337565b8a8a85818110611f2257611f22615b49565b90506020020135898986818110611f3b57611f3b615b49565b9050602002016020810190611f509190615337565b886135a3565b600101611ec4565b5050505050505050505050505050565b60008211611f8f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff8116600090815260176020908152604080832033845290915281208054849290611fbf908490615b5f565b90915550611fd19050338360006137e9565b6040805163ffffffff831681526020810184905233917febeabb72629991c0657c42b5db17113f1eb371cca1f8f30ee73c9f66232c2a629101611512565b612017612765565b565b6000612023612722565b61202f33338585612cab565b506001610c786001601255565b6006546000906001600160a01b036101009091041663b816881661205f60115490565b600a54600b546007546040516001600160e01b031960e087901b16815260048101949094526024840192909252604483015260648201526084015b602060405180830381865afa1580156120b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d69190615a04565b6120e3612722565b6120ef33848484613804565b6115816001601255565b6000546001600160a01b031633146121245760405163303676d360e01b815260040160405180910390fd5b610ede816132c5565b6000805473ffffffffffffffffffffffffffffffffffffffff19163317905561215b888888888888886111db565b6000805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b039290921691909117905550505050505050565b600061219b612722565b6121a3612765565b6121ab612bce565b90506116e66001601255565b6001600160a01b0381166000908152600f6020526040812054819081906121dd85612b7a565b6121e5612bce565b9250925092509193909250565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff1660008115801561223d5750825b905060008267ffffffffffffffff16600114801561225a5750303b155b905081158015612268575080155b1561229f576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156122d357845468ff00000000000000001916680100000000000000001785555b6001600160a01b038616612313576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038816179055831561238657845468ff000000000000000019168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b610ede338260016137e9565b6000546001600160a01b031633146123c55760405163303676d360e01b815260040160405180910390fd5b6013546040517f5c19a95c0000000000000000000000000000000000000000000000000000000081526001600160a01b03838116600483015290911690635c19a95c90602401600060405180830381600087803b15801561242557600080fd5b505af1158015612439573d6000803e3d6000fd5b5050505050565b610ede33826001613a7b565b6000546001600160a01b031633146124775760405163303676d360e01b815260040160405180910390fd5b600d805490829055612487612765565b60408051828152602081018490527f3ae66e204f1f2ae1bf6ea9ddb0331246f344118a48794bc9351cc730a0486658910160405180910390a15050565b6000546001600160a01b031633146124ef5760405163303676d360e01b815260040160405180910390fd5b6001600160a01b0381166125165760405163bca3ab8960e01b815260040160405180910390fd5b6003546040516001600160a01b038084169216907f9859cd0a756b5f08366068b791448fb837581d3b8afc097914d88edbc7bff2a390600090a36003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6006546000906001600160a01b03610100909104166315f240536125a260115490565b600a54600b546040516001600160e01b031960e086901b16815260048101939093526024830191909152604482015260640161209a565b600354604080517f9e106dc700000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c9133918491639e106dc79160048083019260209291908290030181865afa158015612645573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126699190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa1580156126b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d69190615a1d565b61270b576040517ea1222300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601354610ede906001600160a01b03163383612c37565b60026012540361275e576040517f3ee5aeb500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002601255565b6008544290818103612775575050565b600061278060115490565b600a54600b546009546006546040517f15f2405300000000000000000000000000000000000000000000000000000000815260048101869052602481018590526044810184905294955092939192909160009161010090046001600160a01b0316906315f2405390606401602060405180830381865afa158015612808573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061282c9190615a04565b9050600d5481111561286a576040517f8179ac1a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006128768789615a50565b9050600061289260405180602001604052808581525083613a98565b905060006128a08288612f20565b905060006128ae8883615b5f565b905060006128cd6040518060200160405280600754815250848a613ac9565b905060006128dc85898a613ac9565b60088e90556009819055600a849055600b839055604080518d815260208101879052908101829052606081018590529091507f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049060800160405180910390a150505050505050505050505050565b601354611391906001600160a01b03168383612c37565b82612998576040517fc39550bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6129a484848484613aea565b50505050565b60008060008060006129bb88613afe565b95509550509450945094508495506129d33386613b90565b4663ffffffff168163ffffffff16146129ff57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b0384163014612a285760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16612a5e576040516357e05e4960e01b815260040160405180910390fd5b60008711612a7f5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b0389168452909152902054612ab39084615a50565b871115612ad3576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290612b0c908490615b5f565b90915550612b1e90508688600061305b565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fe2cce63addd46173601de3603e8e65733e0aeb61b438aba8c8b139fae31a673291015b60405180910390a45050505050505050565b6001600160a01b0381166000908152600e6020526040812080548203612ba35750600092915050565b6009548154600091612bb491615c78565b9050816001015481612bc69190615c8f565b949350505050565b600c54600090808203612be357505060115490565b6000612bee60115490565b90506000600b54600a5483612c039190615b5f565b612c0d9190615a50565b9050600083612c24670de0b6b3a764000084615c78565b612c2e9190615c8f565b95945050505050565b6040516001600160a01b0383811660248301526044820183905261158191859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050613df4565b6002546040517f17bf120e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152848116604483015260648201849052909116906317bf120e90608401600060405180830381600087803b158015612d2057600080fd5b505af1158015612d34573d6000803e3d6000fd5b50505050816001600160a01b0316836001600160a01b031603612d83576040517f98a79b5400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000836001600160a01b0316856001600160a01b031603612da75750600019612dcf565b506001600160a01b038084166000908152601060209081526040808320938816835292905220545b6000612ddb8383615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e03908590615a50565b6001600160a01b0386166000908152600f602052604081205491925090612e2b908690615b5f565b6001600160a01b038089166000908152600f602052604080822086905591891681522081905590506000198414612e85576001600160a01b038088166000908152601060209081526040808320938c168352929052208390555b856001600160a01b0316876001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef87604051612eca91815260200190565b60405180910390a35050505050505050565b612ee4612722565b612eec612765565b6120ef8360008484613e75565b612f01612722565b612f09612765565b612f15333384846140ef565b506113916001601255565b600080612f2d8484613a98565b9050612bc681614261565b612f40612722565b612f48612765565b816001600160a01b031663a6afed956040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612f8357600080fd5b505af1158015612f97573d6000803e3d6000fd5b50505050612fa88585858585614279565b6124396001601255565b6001600160a01b038116612ff2576040517f2756006b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546040516001600160a01b038084169216907fab81081e8fd2866d32d2dd90fdfa53fa9e0aa3d7eed73c684b6dcc4e2c0369fd90600090a36014805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b613063612722565b61306b612765565b613077338484846140ef565b506115816001601255565b61308a612722565b613092612765565b60008061309f3384614662565b905080601160008282546130b39190615b5f565b9091555050600b546130c6908290615b5f565b600b819055604080518381526020810183905291935033917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a25050610ede6001601255565b6000546001600160a01b031633146131405760405163303676d360e01b815260040160405180910390fd5b806001600160a01b0316632191f92a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561317e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131a29190615a1d565b6131d8576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040516001600160a01b0380841692610100900416907fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f92690600090a3600680546001600160a01b03909216610100027fffffffffffffffffffffff0000000000000000000000000000000000000000ff909216919091179055565b8061328d576040517ffa79c3a000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518181527fe02ca3e718b01d09207861e781e45e3c93060b331a03eed1ab268d12e793583f9060200160405180910390a1601555565b806001600160a01b0316634456eda26040518163ffffffff1660e01b8152600401602060405180830381865afa158015613303573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133279190615a1d565b61335d576040517f3e1fc32700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040516001600160a01b038084169216907ff1e04d73c4304b5ff164f9d10c7473e2a1593b740674a6107975e2a7001c1e5c90600090a36002805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60008060008060006133d788613afe565b95509550509450945094508495506133ef3386613b90565b4663ffffffff168163ffffffff161461341b57604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146134445760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff1661347a576040516357e05e4960e01b815260040160405180910390fd5b6000871161349b5760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546134cf9084615a50565b8711156134ef576040516361ee36eb60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b038916845290915281208054899290613528908490615b5f565b9091555061353a905086886000613588565b6040805163ffffffff84168152602081018990526001600160a01b03808916929088169133917fadedbbedfe5f9c3d3ea9e37eda061744d9ca5a97fbe143765b6164fcee7108569101612b68565b613590612722565b613598612765565b6120ef838383614767565b60008060008060006135b48a613afe565b95509550509450945094508495506135cc3386613b90565b4663ffffffff168163ffffffff16146135f857604051632b25fce360e01b815260040160405180910390fd5b6001600160a01b03841630146136215760405163fb7dcd6b60e01b815260040160405180910390fd5b63ffffffff821660009081526019602052604090205460ff16613657576040516357e05e4960e01b815260040160405180910390fd5b600088116136785760405163fd7850ff60e01b815260040160405180910390fd5b63ffffffff821660009081526016602090815260408083206001600160a01b03891684529091529020546136ac9084615a50565b8811156136cc576040516361ee36eb60e01b815260040160405180910390fd5b6001600160a01b03891633148015906136f75750846001600160a01b0316896001600160a01b031614155b6137145760405163271bd90b60e01b815260040160405180910390fd5b6001600160a01b03871615613729578661372b565b305b63ffffffff831660009081526016602090815260408083206001600160a01b038a168452909152812080549299508a92909190613769908490615b5f565b9091555061377d9050868a8a8a6000612f38565b604080516001600160a01b038b81168252888116602083015263ffffffff851682840152606082018b905291518983169288169133917fc643e0e4d5d1082f6bfc662466c6ee07f745f7725754c136e448452d1f1878679181900360800190a450505050505050505050565b6137f1612722565b6137f9612765565b6120ef8383836149c6565b6002546040517f6765dff90000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0386811660248301528581166044830152848116606483015290911690636765dff990608401600060405180830381600087803b15801561387a57600080fd5b505af115801561388e573d6000803e3d6000fd5b50505050826001600160a01b0316826001600160a01b0316036138c45760405163bca3ab8960e01b815260040160405180910390fd5b60006138e5826040518060200160405280666379da05b60000815250614b5e565b905060006138f38284615a50565b90506000604051806020016040528061390a612bce565b90529050600061391a8285612f20565b9050600081600b5461392c9190615b5f565b600b819055600c54909150613942908690615a50565b600c556001600160a01b0387166000908152600f6020526040902054613969908790615a50565b6001600160a01b038089166000908152600f602052604080822093909355908a1681522054613999908590615b5f565b6001600160a01b03808a166000818152600f602052604090819020939093559151908916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906139ed9088815260200190565b60405180910390a360405185815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3604080518381526020810183905230917fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc5910160405180910390a2505050505050505050565b613a83612722565b613a8b612765565b6120ef8383600084613e75565b6040805160208101909152600081526040518060200160405280613ac0856000015185614b81565b90529392505050565b600080613ad68585613a98565b9050612c2e613ae482614261565b84614b8d565b613af2614b99565b6129a484848484614bdb565b600080600080600080613b1e613b178860006014614c96565b6000614dbe565b9550613b2f613b1788601480614c96565b9450613b48613b418860286020614c96565b6000614e34565b9350613b5a613b418860486020614c96565b9250613b73613b6c8860686004614c96565b6000614e9a565b9150613b85613b6c88606c6004614c96565b905091939550919395565b806001600160a01b0316826001600160a01b031614611391576001600160a01b0380821660009081526018602090815260408083209386168352929052205460ff1680613bea57506000546001600160a01b038381169116145b80613ced5750600354604080517fa872019500000000000000000000000000000000000000000000000000000000815290516001600160a01b03909216916338dd8c2c918591849163a87201959160048083019260209291908290030181865afa158015613c5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c809190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613cc9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ced9190615a1d565b80613dd757506003546040805163a1bd302d60e01b815290516001600160a01b03909216916338dd8c2c918591849163a1bd302d9160048083019260209291908290030181865afa158015613d46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d6a9190615a04565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401602060405180830381865afa158015613db3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dd79190615a1d565b6113915760405163271bd90b60e01b815260040160405180910390fd5b6000613e096001600160a01b03841683614f00565b90508051600014158015613e2e575080806020019051810190613e2c9190615a1d565b155b15611581576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024015b60405180910390fd5b821580613e80575081155b613e9d5760405163bca3ab8960e01b815260040160405180910390fd5b60006040518060200160405280613eb2612bce565b905290506000808515613ed357859150613ecc8387612f20565b9050613ee3565b613edd8584614f0e565b91508490505b81158015613eef575080155b15613f26576040517fbbf3e56400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002546040517f1e32bd9b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0389811660248301526044820185905290911690631e32bd9b90606401600060405180830381600087803b158015613f9357600080fd5b505af1158015613fa7573d6000803e3d6000fd5b5050505080613fb560115490565b1015613fed576040517f14ec588c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600c54613ffb9190615a50565b600c556001600160a01b0387166000908152600f6020526040902054614022908390615a50565b6001600160a01b0388166000908152600f6020526040902055831561404b5761404b878261294a565b806011600082825461405d9190615a50565b909155505060405182815230906001600160a01b038916907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a360408051828152602081018490526001600160a01b038916917fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a929910160405180910390a250505050505050565b6002546040517fc321fbcc0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038581166024830152600092169063c321fbcc90604401600060405180830381600087803b15801561415657600080fd5b505af115801561416a573d6000803e3d6000fd5b50505050600061417985612b7a565b90506000600019851461418c578461418e565b815b905060008461419d57816141a7565b6141a78883614662565b905080601160008282546141bb9190615b5f565b90915550600090506141cd8285615a50565b9050600082600a546141df9190615a50565b6001600160a01b038a81166000818152600e6020908152604091829020878155600954600190910155600a859055815188815290810187905290810184905292935091908c16907f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a19060600160405180910390a3509098975050505050505050565b8051600090610c7890670de0b6b3a764000090615c8f565b846001600160a01b0316846001600160a01b0316036142ab5760405163bca3ab8960e01b815260040160405180910390fd5b6000831180156142bd57506000198314155b6142da5760405163bca3ab8960e01b815260040160405180910390fd5b6002546040517fb50ce7620000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0384811660248301528681166044830152606482018690529091169063b50ce7629060840160006040518083038186803b15801561434d57600080fd5b505afa158015614361573d6000803e3d6000fd5b5050505061436c4290565b826001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156143aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143ce9190615a04565b14614405576040517fdf70808000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614413868686856140ef565b6002546040517fc488847b0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038681166024830152604482018490529293506000929091169063c488847b90606401602060405180830381865afa158015614489573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144ad9190615a04565b6040516370a0823160e01b81526001600160a01b03888116600483015291925082918616906370a0823190602401602060405180830381865afa1580156144f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061451c9190615a04565b1015614554576040517f7286c37500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b306001600160a01b038516036145755761457030888884613804565b6145f9565b6040517fb2a02ff10000000000000000000000000000000000000000000000000000000081526001600160a01b03888116600483015287811660248301526044820183905285169063b2a02ff190606401600060405180830381600087803b1580156145e057600080fd5b505af11580156145f4573d6000803e3d6000fd5b505050505b836001600160a01b0316866001600160a01b0316886001600160a01b03167f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb528585604051614651929190918252602082015260400190565b60405180910390a450505050505050565b6013546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156146af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146d39190615a04565b6013549091506146ee906001600160a01b0316853086614f2c565b6013546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015614737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061475b9190615a04565b9050612c2e8282615a50565b6002546040517fc0f1ee090000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0385811660248301529091169063c0f1ee0990604401600060405180830381600087803b1580156147cd57600080fd5b505af11580156147e1573d6000803e3d6000fd5b50505050600060405180602001604052806147fa612bce565b9052905060008261480b5783614815565b6148158585614662565b905080601160008282546148299190615b5f565b909155506000905061483b8284614f0e565b9050600c54600003614889576103e8600c81905560008052600f6020527ff4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec3758190556148869082615a50565b90505b80600c546148979190615b5f565b600c556001600160a01b0386166000908152600f60205260409020546148be908290615b5f565b6001600160a01b0387166000818152600f602090815260409182902093909355805185815292830184905290917f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f910160405180910390a26040518181526001600160a01b0387169030907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a36002546040517f0d926fc80000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0390911690630d926fc89060240160006040518083038186803b1580156149b257600080fd5b505afa158015611057573d6000803e3d6000fd5b6002546040517f50795f8a0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03858116602483015260448201859052909116906350795f8a90606401600060405180830381600087803b158015614a3357600080fd5b505af1158015614a47573d6000803e3d6000fd5b5050505081614a5560115490565b1015614a8d576040517fcd85b93e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000614a9884612b7a565b90506000614aa68483615b5f565b9050600084600a54614ab89190615b5f565b6001600160a01b0387166000908152600e60205260409020838155600954600190910155600a81905590508315614af357614af3868661294a565b8460116000828254614b059190615a50565b909155505060408051868152602081018490529081018290526001600160a01b038716907f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab809060600160405180910390a2505050505050565b6000670de0b6b3a7640000614b77848460000151614b81565b610c759190615c8f565b6000610c758284615c78565b6000610c758284615b5f565b6014546001600160a01b0316612017576040517ff242df5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6014546015546040516001600160a01b039092169163ab750e75918591859190600290614c0b908b908b90615cb1565b602060405180830381855afa158015614c28573d6000803e3d6000fd5b5050506040513d601f19601f82011682018060405250810190614c4b9190615a04565b6040518563ffffffff1660e01b8152600401614c6a9493929190615cc1565b60006040518083038186803b158015614c8257600080fd5b505afa15801561128c573d6000803e3d6000fd5b606081614ca481601f615b5f565b1015614cf25760405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401613e6c565b614cfc8284615b5f565b84511015614d4c5760405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401613e6c565b606082158015614d6b5760405191506000825260208201604052614db5565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015614da4578051835260209283019201614d8c565b5050858452601f01601f1916604052505b50949350505050565b6000614dcb826014615b5f565b83511015614e1b5760405162461bcd60e51b815260206004820152601560248201527f746f416464726573735f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b5001602001516c01000000000000000000000000900490565b6000614e41826020615b5f565b83511015614e915760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e647300000000000000000000006044820152606401613e6c565b50016020015190565b6000614ea7826004615b5f565b83511015614ef75760405162461bcd60e51b815260206004820152601460248201527f746f55696e7433325f6f75744f66426f756e64730000000000000000000000006044820152606401613e6c565b50016004015190565b6060610c7583836000614f65565b6000610c75614f2584670de0b6b3a7640000614b81565b835161501b565b6040516001600160a01b0384811660248301528381166044830152606482018390526129a49186918216906323b872dd90608401612c64565b606081471015614fa3576040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152602401613e6c565b600080856001600160a01b03168486604051614fbf9190615d01565b60006040518083038185875af1925050503d8060008114614ffc576040519150601f19603f3d011682016040523d82523d6000602084013e615001565b606091505b5091509150615011868383615027565b9695505050505050565b6000610c758284615c8f565b60608261503c576150378261509c565b61146e565b815115801561505357506001600160a01b0384163b155b15615095576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401613e6c565b508061146e565b8051156150ac5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b838110156150f95781810151838201526020016150e1565b50506000910152565b6000815180845261511a8160208601602086016150de565b601f01601f19169290920160200192915050565b602081526000610c756020830184615102565b6001600160a01b0381168114610ede57600080fd5b80356111c781615141565b803563ffffffff811681146111c757600080fd5b6000806040838503121561518857600080fd5b823561519381615141565b91506151a160208401615161565b90509250929050565b6000602082840312156151bc57600080fd5b5035919050565b60008083601f8401126151d557600080fd5b50813567ffffffffffffffff8111156151ed57600080fd5b60208301915083602082850101111561520557600080fd5b9250929050565b60008083601f84011261521e57600080fd5b50813567ffffffffffffffff81111561523657600080fd5b6020830191508360208260051b850101111561520557600080fd5b60008060008060008060006080888a03121561526c57600080fd5b873567ffffffffffffffff81111561528357600080fd5b61528f8a828b016151c3565b909850965050602088013567ffffffffffffffff8111156152af57600080fd5b6152bb8a828b016151c3565b909650945050604088013567ffffffffffffffff8111156152db57600080fd5b6152e78a828b0161520c565b90945092505060608801356152fb81615141565b8091505092959891949750929550565b6000806040838503121561531e57600080fd5b823561532981615141565b946020939093013593505050565b60006020828403121561534957600080fd5b813561146e81615141565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561539357615393615354565b604052919050565b60008067ffffffffffffffff8411156153b6576153b6615354565b50601f8301601f19166020016153cb8161536a565b9150508281528383830111156153e057600080fd5b828260208301376000602084830101529392505050565b600082601f83011261540857600080fd5b610c758383356020850161539b565b803560ff811681146111c757600080fd5b600080600080600080600060e0888a03121561544357600080fd5b873561544e81615141565b9650602088013561545e81615141565b9550604088013561546e81615141565b945060608801359350608088013567ffffffffffffffff81111561549157600080fd5b61549d8a828b016153f7565b93505060a088013567ffffffffffffffff8111156154ba57600080fd5b6154c68a828b016153f7565b9250506154d560c08901615417565b905092959891949750929550565b6000806000606084860312156154f857600080fd5b833561550381615141565b9250602084013561551381615141565b929592945050506040919091013590565b6000806040838503121561553757600080fd5b823591506151a160208401615161565b6000806040838503121561555a57600080fd5b61556383615161565b9150602083013561557381615141565b809150509250929050565b60008060006060848603121561559357600080fd5b833561559e81615141565b92506020840135915060408401356155b581615141565b809150509250925092565b8015158114610ede57600080fd5b600080604083850312156155e157600080fd5b82356155ec81615141565b91506020830135615573816155c0565b6000806040838503121561560f57600080fd5b823561556381615141565b6000806000806000806000806000806101408b8d03121561563a57600080fd5b6156438b615156565b995061565160208c01615156565b985061565f60408c01615156565b975060608b0135965060808b013567ffffffffffffffff81111561568257600080fd5b61568e8d828e016153f7565b96505060a08b013567ffffffffffffffff8111156156ab57600080fd5b6156b78d828e016153f7565b9550506156c660c08c01615417565b93506156d460e08c01615156565b92506156e36101008c01615156565b91506156f26101208c01615156565b90509295989b9194979a5092959850565b60006020828403121561571557600080fd5b610c7582615161565b6000806040838503121561573157600080fd5b6155ec83615161565b60008060008060008060c0878903121561575357600080fd5b863561575e81615141565b9550602087013561576e81615141565b945060408701359350606087013567ffffffffffffffff81111561579157600080fd5b61579d89828a016153f7565b935050608087013567ffffffffffffffff8111156157ba57600080fd5b6157c689828a016153f7565b9250506157d560a08801615417565b90509295509295509295565b600080600080600080600080600080600060c08c8e03121561580257600080fd5b8b3567ffffffffffffffff81111561581957600080fd5b6158258e828f016151c3565b909c509a505060208c013567ffffffffffffffff81111561584557600080fd5b6158518e828f016151c3565b909a5098505060408c013567ffffffffffffffff81111561587157600080fd5b61587d8e828f0161520c565b90985096505060608c013567ffffffffffffffff81111561589d57600080fd5b6158a98e828f0161520c565b90965094505060808c013567ffffffffffffffff8111156158c957600080fd5b6158d58e828f0161520c565b90945092506158e8905060a08d01615156565b90509295989b509295989b9093969950565b600080600080600080600080610100898b03121561591757600080fd5b883561592281615141565b9750602089013561593281615141565b9650604089013561594281615141565b955060608901359450608089013567ffffffffffffffff81111561596557600080fd5b6159718b828c016153f7565b94505060a089013567ffffffffffffffff81111561598e57600080fd5b61599a8b828c016153f7565b9350506159a960c08a01615417565b915060e08901356159b981615141565b809150509295985092959890939650565b600181811c908216806159de57607f821691505b6020821081036159fe57634e487b7160e01b600052602260045260246000fd5b50919050565b600060208284031215615a1657600080fd5b5051919050565b600060208284031215615a2f57600080fd5b815161146e816155c0565b634e487b7160e01b600052601160045260246000fd5b81810381811115610c7857610c78615a3a565b600060208284031215615a7557600080fd5b813567ffffffffffffffff811115615a8c57600080fd5b8201601f81018413615a9d57600080fd5b803567ffffffffffffffff811115615ab757615ab7615354565b8060051b615ac76020820161536a565b91825260208184018101929081019087841115615ae357600080fd5b6020850192505b83831015615b3e57823567ffffffffffffffff811115615b0957600080fd5b8501603f81018913615b1a57600080fd5b615b2c8960208301356040840161539b565b83525060209283019290910190615aea565b979650505050505050565b634e487b7160e01b600052603260045260246000fd5b80820180821115610c7857610c78615a3a565b601f82111561158157806000526020600020601f840160051c81016020851015615b995750805b601f840160051c820191505b818110156124395760008155600101615ba5565b815167ffffffffffffffff811115615bd357615bd3615354565b615be781615be184546159ca565b84615b72565b6020601f821160018114615c1b5760008315615c035750848201515b600019600385901b1c1916600184901b178455612439565b600084815260208120601f198516915b82811015615c4b5787850151825560209485019460019092019101615c2b565b5084821015615c695786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b8082028115828204841417610c7857610c78615a3a565b600082615cac57634e487b7160e01b600052601260045260246000fd5b500490565b8183823760009101908152919050565b606081528360608201528385608083013760006080858301015260006080601f19601f870116830101905083602083015282604083015295945050505050565b60008251615d138184602087016150de565b919091019291505056fea2646970667358221220724ffecd1dda03b6a8f003dfa58cd82a8ac6d25418fd46d9382ac52b8b1e24df64736f6c634300081c0033

Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

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