MON Price: $0.021256 (+13.09%)

Contract

0xA6fdD71773AA01eF007fbFBe263D1170CCd4e812

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MagmaBoostedDelegate

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
prague EvmVersion
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.20;

import "../PErc20Delegate.sol";
import "../PeridottrollerInterface.sol";
import "../InterestRateModel.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IMagma {
    // Deposits (synchronous)
    function depositWMON(
        uint256 assets,
        address receiver,
        uint256 referralId
    ) external returns (uint256 shares);

    function depositMON(
        address receiver,
        uint256 referralId
    ) external payable returns (uint256 shares);

    // Redemptions (asynchronous - two step)
    function requestRedeem(
        uint256 shares,
        address controller,
        address owner
    ) external returns (uint256 requestId);

    function redeemMON(
        uint256 requestId,
        address controller,
        address receiver
    ) external returns (uint256 assets);

    function redeem(
        uint256 requestId,
        address controller,
        address receiver
    ) external returns (uint256 assets);

    // ERC-4626 View functions
    function balanceOf(address account) external view returns (uint256 shares);

    function convertToAssets(
        uint256 shares
    ) external view returns (uint256 assets);

    function convertToShares(
        uint256 assets
    ) external view returns (uint256 shares);

    function totalAssets() external view returns (uint256);

    // Request tracking
    function ownerRequestId(
        address owner
    ) external view returns (uint256 requestId);
}

/**
 * @title MagmaBoostedDelegate
 * @notice Compound-style delegate for a Magma-boosted market.
 * @dev Underlying is WMON. Idle WMON is deposited into Magma to earn gMON (Monad LST) yield.
 *      Due to Magma's asynchronous redemption design (Monad unbonding period), this implementation
 *      maintains a buffer of gMON shares for immediate withdrawals. If buffer is insufficient,
 *      withdrawals may fail until admin rebalances or async redemptions complete.
 */
contract MagmaBoostedDelegate is PErc20Delegate {
    using SafeERC20 for IERC20;

    uint256 internal constant MANTISSA_ONE = 1e18;

    /// @notice Magma contract (gMON LST).
    IMagma public magmaVault;

    /// @notice Fraction of total managed assets to keep as liquid gMON buffer (scaled by 1e18).
    /// @dev Higher buffer = more immediate withdrawal capacity, but less capital efficiency.
    uint256 public vaultBufferMantissa;

    /// @notice When true, deposits are paused and funds should be withdrawn from Magma.
    bool public vaultPaused;

    /// @notice Pending redemption request ID from Magma (0 if none).
    uint256 public pendingRedemptionId;

    /// @notice Referral ID for Magma points (default: 0).
    uint256 public referralId;

    event VaultBufferUpdated(uint256 previousMantissa, uint256 newMantissa);
    event VaultPausedUpdated(bool paused);
    event VaultDeposited(uint256 assets, uint256 shares);
    event RedemptionRequested(uint256 requestId, uint256 shares);
    event RedemptionCompleted(uint256 requestId, uint256 assets);
    event ReferralIdUpdated(uint256 newReferralId);

    /**
     * @notice Called when becoming the implementation for a delegator.
     */
    function _becomeImplementation(bytes memory data) public override {
        require(msg.sender == admin, "only admin");
        if (data.length > 0) {
            (address vault_, uint256 buffer_) = abi.decode(
                data,
                (address, uint256)
            );
            if (vault_ != address(0)) {
                magmaVault = IMagma(vault_);
                vaultBufferMantissa = buffer_;
            }
        }
    }

    /**
     * @notice Admin function to set the Magma vault.
     */
    function _setMagmaVault(address vault_, uint256 buffer_) external {
        require(msg.sender == admin, "only admin");
        require(vault_ != address(0), "zero vault");
        magmaVault = IMagma(vault_);
        vaultBufferMantissa = buffer_;
        emit VaultBufferUpdated(0, buffer_);
    }

    /**
     * @notice Initialize delegate storage.
     */
    function initialize(
        address underlying_,
        PeridottrollerInterface peridottroller_,
        InterestRateModel interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        IMagma magmaVault_,
        uint256 vaultBufferMantissa_
    ) public {
        require(accrualBlockNumber == 0 && borrowIndex == 0, "already init");

        admin = payable(msg.sender);
        magmaVault = magmaVault_;
        vaultBufferMantissa = vaultBufferMantissa_;

        super.initialize(
            underlying_,
            peridottroller_,
            interestRateModel_,
            initialExchangeRateMantissa_,
            name_,
            symbol_,
            decimals_
        );
    }

    // Admin setters
    function _setVaultBuffer(uint256 newMantissa) external {
        require(msg.sender == admin, "only admin");
        require(newMantissa <= MANTISSA_ONE, "invalid buffer");
        uint256 prev = vaultBufferMantissa;
        vaultBufferMantissa = newMantissa;
        emit VaultBufferUpdated(prev, newMantissa);
        _rebalanceVault();
    }

    function _setVaultPaused(bool pause) external {
        require(msg.sender == admin, "only admin");
        if (pause == vaultPaused) return;
        vaultPaused = pause;
        emit VaultPausedUpdated(pause);
        // Note: When paused, we can't immediately pull from Magma due to async redemptions
        // Admin must manually request redemptions via _requestRedemption
    }

    function _setReferralId(uint256 newReferralId) external {
        require(msg.sender == admin, "only admin");
        referralId = newReferralId;
        emit ReferralIdUpdated(newReferralId);
    }

    /**
     * @notice Admin function to request redemption from Magma (step 1 of async withdrawal).
     * @param shares Amount of gMON shares to redeem.
     */
    function _requestRedemption(uint256 shares) external {
        require(msg.sender == admin, "only admin");
        require(pendingRedemptionId == 0, "redemption pending");

        uint256 requestId = magmaVault.requestRedeem(
            shares,
            address(this),
            address(this)
        );
        pendingRedemptionId = requestId;
        emit RedemptionRequested(requestId, shares);
    }

    /**
     * @notice Admin function to complete redemption from Magma (step 2 of async withdrawal).
     * @dev Can only be called after WITHDRAWAL_DELAY epochs have passed.
     */
    function _completeRedemption() external {
        require(msg.sender == admin, "only admin");
        uint256 requestId = pendingRedemptionId;
        require(requestId != 0, "no pending redemption");

        // Complete redemption - this returns WMON to this contract
        uint256 assets = magmaVault.redeem(
            requestId,
            address(this),
            address(this)
        );
        pendingRedemptionId = 0;

        emit RedemptionCompleted(requestId, assets);
        _rebalanceVault();
    }

    // Overridden cash/view hooks
    function getCashPrior() internal view override returns (uint256) {
        return super.getCashPrior() + _vaultWithdrawable();
    }

    function doTransferIn(
        address from,
        uint256 amount
    ) internal override returns (uint256) {
        uint256 actual = super.doTransferIn(from, amount);
        _rebalanceVault();
        return actual;
    }

    function doTransferOut(
        address payable to,
        uint256 amount
    ) internal override {
        _ensureLocalLiquidity(amount);
        super.doTransferOut(to, amount);
        _rebalanceVault();
    }

    // Vault helpers
    function _rebalanceVault() internal {
        if (vaultPaused) return;

        uint256 localCash = super.getCashPrior();
        uint256 vaultAssets = _vaultAssets();
        uint256 total = localCash + vaultAssets;
        if (total == 0) return;

        uint256 target = (total * vaultBufferMantissa) / MANTISSA_ONE;

        if (localCash > target) {
            uint256 toDeposit = localCash - target;
            _depositToVault(toDeposit);
        }
        // Note: We cannot pull from vault immediately due to async redemptions
        // Admin must manually request and complete redemptions if buffer is low
    }

    function _ensureLocalLiquidity(uint256 amount) internal view {
        uint256 localCash = super.getCashPrior();
        require(
            localCash >= amount,
            "insufficient buffer - admin must complete redemption"
        );
    }

    function _depositToVault(uint256 amount) internal {
        if (amount == 0 || vaultPaused) return;

        IERC20(underlying).forceApprove(address(magmaVault), amount);
        uint256 shares = magmaVault.depositWMON(
            amount,
            address(this),
            referralId
        );
        emit VaultDeposited(amount, shares);
    }

    function _vaultAssets() internal view returns (uint256) {
        uint256 shares = magmaVault.balanceOf(address(this));
        if (shares == 0) return 0;
        return magmaVault.convertToAssets(shares);
    }

    function _vaultWithdrawable() internal view returns (uint256) {
        // Since redemptions are async, we can only count what's immediately available
        // which is the gMON balance converted to assets (optimistic)
        return _vaultAssets();
    }

    /**
     * @notice View function to check if a pending redemption can be completed.
     */
    function canCompleteRedemption() external view returns (bool) {
        uint256 requestId = pendingRedemptionId;
        if (requestId == 0) return false;
        // Note: Magma doesn't expose a "isClaimable" function
        // Admin should track epochs and call _completeRedemption after WITHDRAWAL_DELAY
        return true;
    }
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PErc20.sol";

/**
 * @title Peridot's PErc20Delegate Contract
 * @notice PTokens which wrap an EIP-20 underlying and are delegated to
 * @author Peridot
 */
contract PErc20Delegate is PErc20, CDelegateInterface {
    /**
     * @notice Construct an empty delegate
     */
    constructor() {}

    /**
     * @notice Called by the delegator on a delegate to initialize it for duty
     * @param data The encoded bytes data for any initialization
     */
    function _becomeImplementation(bytes memory data) public virtual override {
        // Shh -- currently unused
        data;

        // Shh -- we don't ever want this hook to be marked pure
        if (false) {
            implementation = address(0);
        }

        require(msg.sender == admin, "only the admin may call _becomeImplementation");
    }

    /**
     * @notice Called by the delegator on a delegate to forfeit its responsibility
     */
    function _resignImplementation() public virtual override {
        // Shh -- we don't ever want this hook to be marked pure
        if (false) {
            implementation = address(0);
        }

        require(msg.sender == admin, "only the admin may call _resignImplementation");
    }
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PToken.sol";

abstract contract PeridottrollerInterface {
    /// @notice Indicator that this is a Peridottroller contract (for inspection)
    bool public constant isPeridottroller = true;

    /**
     * Assets You Are In **
     */
    function enterMarkets(
        address[] calldata pTokens
    ) external virtual returns (uint256[] memory);

    function exitMarket(address pToken) external virtual returns (uint256);

    /**
     * Policy Hooks **
     */
    function mintAllowed(
        address pToken,
        address minter,
        uint256 mintAmount
    ) external virtual returns (uint256);

    function mintVerify(
        address pToken,
        address minter,
        uint256 mintAmount,
        uint256 mintTokens
    ) external virtual;

    function redeemAllowed(
        address pToken,
        address redeemer,
        uint256 redeemTokens
    ) external virtual returns (uint256);

    function redeemVerify(
        address pToken,
        address redeemer,
        uint256 redeemAmount,
        uint256 redeemTokens
    ) external virtual;

    function borrowAllowed(
        address pToken,
        address borrower,
        uint256 borrowAmount
    ) external virtual returns (uint256);

    function borrowVerify(
        address pToken,
        address borrower,
        uint256 borrowAmount
    ) external virtual;

    function repayBorrowAllowed(
        address pToken,
        address payer,
        address borrower,
        uint256 repayAmount
    ) external virtual returns (uint256);

    function repayBorrowVerify(
        address pToken,
        address payer,
        address borrower,
        uint256 repayAmount,
        uint256 borrowerIndex
    ) external virtual;

    function liquidateBorrowAllowed(
        address pTokenBorrowed,
        address pTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount
    ) external virtual returns (uint256);

    function liquidateBorrowVerify(
        address pTokenBorrowed,
        address pTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount,
        uint256 seizeTokens
    ) external virtual;

    function seizeAllowed(
        address pTokenCollateral,
        address pTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external virtual returns (uint256);

    function seizeVerify(
        address pTokenCollateral,
        address pTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external virtual;

    function transferAllowed(
        address pToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external virtual returns (uint256);

    function transferVerify(
        address pToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external virtual;

    /**
     * Liquidity/Liquidation Calculations **
     */
    function liquidateCalculateSeizeTokens(
        address pTokenBorrowed,
        address pTokenCollateral,
        uint256 repayAmount
    ) external view virtual returns (uint256, uint256);

    function getAccountLiquidity(
        address account
    ) external view virtual returns (uint256, uint256, uint256);

    function getAllMarkets() external view virtual returns (PToken[] memory);
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

/**
 * @title Peridot's InterestRateModel Interface
 * @author Peridot
 */
abstract contract InterestRateModel {
    /// @notice Indicator that this is an InterestRateModel contract (for inspection)
    bool public constant isInterestRateModel = true;

    /**
     * @notice Calculates the current borrow interest rate per block
     * @param cash The total amount of cash the market has
     * @param borrows The total amount of borrows the market has outstanding
     * @param reserves The total amount of reserves the market has
     * @return The borrow rate per block (as a percentage, and scaled by 1e18)
     */
    function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external view virtual returns (uint256);

    /**
     * @notice Calculates the current supply interest rate per block
     * @param cash The total amount of cash the market has
     * @param borrows The total amount of borrows the market has outstanding
     * @param reserves The total amount of reserves the market has
     * @param reserveFactorMantissa The current reserve factor the market has
     * @return The supply rate per block (as a percentage, and scaled by 1e18)
     */
    function getSupplyRate(uint256 cash, uint256 borrows, uint256 reserves, uint256 reserveFactorMantissa)
        external
        view
        virtual
        returns (uint256);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PToken.sol";

interface PeridotLike {
    function delegate(address delegatee) external;
}

/**
 * @title Peridot's PErc20 Contract
 * @notice PTokens which wrap an EIP-20 underlying
 * @author Peridot
 */
contract PErc20 is PToken, PErc20Interface {
    /**
     * @notice Initialize the new money market
     * @param underlying_ The address of the underlying asset
     * @param peridottroller_ The address of the Peridottroller
     * @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_,
        PeridottrollerInterface peridottroller_,
        InterestRateModel interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) public {
        // PToken initialize does the bulk of the work
        super.initialize(peridottroller_, interestRateModel_, initialExchangeRateMantissa_, name_, symbol_, decimals_);

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

    /**
     * User Interface **
     */

    /**
     * @notice Sender supplies assets into the market and receives pTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param mintAmount The amount of the underlying asset to supply
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function mint(uint256 mintAmount) external override returns (uint256) {
        mintInternal(mintAmount);
        return NO_ERROR;
    }

    /**
     * @notice Sender redeems pTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemTokens The number of pTokens to redeem into underlying
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeem(uint256 redeemTokens) external override returns (uint256) {
        redeemInternal(redeemTokens);
        return NO_ERROR;
    }

    /**
     * @notice Sender redeems pTokens 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
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function redeemUnderlying(uint256 redeemAmount) external override returns (uint256) {
        redeemUnderlyingInternal(redeemAmount);
        return NO_ERROR;
    }

    /**
     * @notice Sender borrows assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function borrow(uint256 borrowAmount) external override returns (uint256) {
        borrowInternal(borrowAmount);
        return NO_ERROR;
    }

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function repayBorrow(uint256 repayAmount) external override returns (uint256) {
        repayBorrowInternal(repayAmount);
        return NO_ERROR;
    }

    /**
     * @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
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function repayBorrowBehalf(address borrower, uint256 repayAmount) external override returns (uint256) {
        repayBorrowBehalfInternal(borrower, repayAmount);
        return NO_ERROR;
    }

    /**
     * @notice The sender liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this pToken to be liquidated
     * @param repayAmount The amount of the underlying borrowed asset to repay
     * @param pTokenCollateral The market in which to seize collateral from the borrower
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function liquidateBorrow(address borrower, uint256 repayAmount, PTokenInterface pTokenCollateral)
        external
        override
        returns (uint256)
    {
        liquidateBorrowInternal(borrower, repayAmount, pTokenCollateral);
        return NO_ERROR;
    }

    /**
     * @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(EIP20NonStandardInterface token) external override {
        require(msg.sender == admin, "PErc20::sweepToken: only admin can sweep tokens");
        require(address(token) != underlying, "PErc20::sweepToken: can not sweep underlying token");
        uint256 balance = token.balanceOf(address(this));
        token.transfer(admin, balance);
    }

    /**
     * @notice The sender adds to reserves.
     * @param addAmount The amount fo underlying token to add as reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _addReserves(uint256 addAmount) external override returns (uint256) {
        return _addReservesInternal(addAmount);
    }

    /**
     * Flash Loan Support **
     */

    /**
     * @notice Returns the underlying token address for flash loans
     * @return The address of the underlying ERC20 token
     */
    function getUnderlyingAddress() internal view override returns (address) {
        return underlying;
    }

    /**
     * Safe Token **
     */

    /**
     * @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) {
        EIP20Interface token = EIP20Interface(underlying);
        return token.balanceOf(address(this));
    }

    /**
     * @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and reverts in that case.
     *      This will revert due to insufficient balance or insufficient allowance.
     *      This function returns the actual amount received,
     *      which may be less than `amount` if there is a fee attached to the transfer.
     *
     *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
     *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
     */
    function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) {
        // Read from storage once
        address underlying_ = underlying;
        EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying_);
        uint256 balanceBefore = EIP20Interface(underlying_).balanceOf(address(this));
        token.transferFrom(from, address(this), amount);

        bool success;
        assembly {
            switch returndatasize()
            case 0 {
                // This is a non-standard ERC-20
                success := not(0) // set success to true
            }
            case 32 {
                // This is a peridotliant ERC-20
                returndatacopy(0, 0, 32)
                success := mload(0) // Set `success = returndata` of override external call
            }
            default {
                // This is an excessively non-peridotliant ERC-20, revert.
                revert(0, 0)
            }
        }
        require(success, "TOKEN_TRANSFER_IN_FAILED");

        // Calculate the amount that was *actually* transferred
        uint256 balanceAfter = EIP20Interface(underlying_).balanceOf(address(this));
        return balanceAfter - balanceBefore; // underflow already checked above, just subtract
    }

    /**
     * @dev Similar to EIP20 transfer, except it handles a False success from `transfer` and returns an explanatory
     *      error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
     *      insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
     *      it is >= amount, this should not revert in normal conditions.
     *
     *      Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
     *            See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
     */
    function doTransferOut(address payable to, uint256 amount) internal virtual override {
        EIP20NonStandardInterface token = EIP20NonStandardInterface(underlying);
        token.transfer(to, amount);

        bool success;
        assembly {
            switch returndatasize()
            case 0 {
                // This is a non-standard ERC-20
                success := not(0) // set success to true
            }
            case 32 {
                // This is a peridotliant ERC-20
                returndatacopy(0, 0, 32)
                success := mload(0) // Set `success = returndata` of override external call
            }
            default {
                // This is an excessively non-peridotliant ERC-20, revert.
                revert(0, 0)
            }
        }
        require(success, "TOKEN_TRANSFER_OUT_FAILED");
    }

    /**
     * @notice Admin call to delegate the votes of the PERIDOT-like underlying
     * @param peridotLikeDelegatee The address to delegate votes to
     * @dev PTokens whose underlying are not PeridotLike should revert here
     */
    function _delegatePeridotLikeTo(address peridotLikeDelegatee) external {
        require(msg.sender == admin, "only the admin may set the peridot-like delegate");
        PeridotLike(underlying).delegate(peridotLikeDelegatee);
    }
}

File 7 of 19 : PToken.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PeridottrollerInterface.sol";
import "./PTokenInterfaces.sol";
import "./ErrorReporter.sol";
import "./EIP20Interface.sol";
import "./InterestRateModel.sol";
import "./ExponentialNoError.sol";
import "./PeridottrollerStorage.sol";

/**
 * @title Peridot's PToken Contract
 * @notice Abstract base for PTokens
 * @author Peridot
 */
abstract contract PToken is
    PTokenInterface,
    ExponentialNoError,
    TokenErrorReporter,
    IERC3156FlashLender
{
    /**
     * @notice Initialize the money market
     * @param peridottroller_ The address of the Peridottroller
     * @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(
        PeridottrollerInterface peridottroller_,
        InterestRateModel interestRateModel_,
        uint256 initialExchangeRateMantissa_,
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) public {
        require(msg.sender == admin, "only admin may initialize the market");
        require(
            accrualBlockNumber == 0 && borrowIndex == 0,
            "market may only be initialized once"
        );

        // Set initial exchange rate
        initialExchangeRateMantissa = initialExchangeRateMantissa_;
        require(
            initialExchangeRateMantissa > 0,
            "initial exchange rate must be greater than zero."
        );

        // Set the peridottroller
        uint256 err = _setPeridottroller(peridottroller_);
        require(err == NO_ERROR, "setting peridottroller failed");

        // Initialize block number and borrow index (block number mocks depend on peridottroller being set)
        accrualBlockNumber = getBlockNumber();
        borrowIndex = mantissaOne;

        // Set the interest rate model (depends on block number / borrow index)
        err = _setInterestRateModelFresh(interestRateModel_);
        require(err == NO_ERROR, "setting interest rate model failed");

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

        // Set default flash loan parameters
        // 0.05% fee, up to 30% of available cash, enabled by default
        flashLoanFeeBps = 5;
        maxFlashLoanRatio = 3000; // 30%
        flashLoansPaused = false;

        // The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
        _notEntered = true;
    }

    /**
     * @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
     * @return 0 if the transfer succeeded, else revert
     */
    function transferTokens(
        address spender,
        address src,
        address dst,
        uint256 tokens
    ) internal returns (uint256) {
        /* Fail if transfer not allowed */
        uint256 allowed = peridottroller.transferAllowed(
            address(this),
            src,
            dst,
            tokens
        );
        if (allowed != 0) {
            revert TransferPeridottrollerRejection(allowed);
        }

        /* Do not allow self-transfers */
        if (src == dst) {
            revert TransferNotAllowed();
        }

        /* 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 srpTokensNew = accountTokens[src] - tokens;
        uint256 dstTokensNew = accountTokens[dst] + tokens;

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

        accountTokens[src] = srpTokensNew;
        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);

        // unused function
        // peridottroller.transferVerify(address(this), src, dst, tokens);

        return NO_ERROR;
    }

    /**
     * @notice Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return Whether or not the transfer succeeded
     */
    function transfer(
        address dst,
        uint256 amount
    ) external override nonReentrant returns (bool) {
        return transferTokens(msg.sender, msg.sender, dst, amount) == NO_ERROR;
    }

    /**
     * @notice Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return Whether or not the transfer succeeded
     */
    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external override nonReentrant returns (bool) {
        return transferTokens(msg.sender, src, dst, amount) == NO_ERROR;
    }

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved (uint256.max means infinite)
     * @return Whether or not the approval succeeded
     */
    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;
    }

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return The number of tokens allowed to be spent (-1 means infinite)
     */
    function allowance(
        address owner,
        address spender
    ) external view override returns (uint256) {
        return transferAllowances[owner][spender];
    }

    /**
     * @notice Get the token balance of the `owner`
     * @param owner The address of the account to query
     * @return The number of tokens owned by `owner`
     */
    function balanceOf(address owner) external view override returns (uint256) {
        return accountTokens[owner];
    }

    /**
     * @notice Get the underlying balance of the `owner`
     * @dev This also accrues interest in a transaction
     * @param owner The address of the account to query
     * @return The amount of underlying owned by `owner`
     */
    function balanceOfUnderlying(
        address owner
    ) external override returns (uint256) {
        Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
        return mul_ScalarTruncate(exchangeRate, accountTokens[owner]);
    }

    /**
     * @notice Get a snapshot of the account's balances, and the cached exchange rate
     * @dev This is used by peridottroller to more efficiently perform liquidity checks.
     * @param account Address of the account to snapshot
     * @return (possible error, token balance, borrow balance, exchange rate mantissa)
     */
    function getAccountSnapshot(
        address account
    ) external view override returns (uint256, uint256, uint256, uint256) {
        return (
            NO_ERROR,
            accountTokens[account],
            borrowBalanceStoredInternal(account),
            exchangeRateStoredInternal()
        );
    }

    /**
     * @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.number;
    }

    /**
     * @notice Returns the current per-block borrow interest rate for this pToken
     * @return The borrow interest rate per block, scaled by 1e18
     */
    function borrowRatePerBlock() external view override returns (uint256) {
        return
            interestRateModel.getBorrowRate(
                getCashPrior(),
                totalBorrows,
                totalReserves
            );
    }

    /**
     * @notice Returns the current per-block supply interest rate for this pToken
     * @return The supply interest rate per block, scaled by 1e18
     */
    function supplyRatePerBlock() external view override returns (uint256) {
        return
            interestRateModel.getSupplyRate(
                getCashPrior(),
                totalBorrows,
                totalReserves,
                reserveFactorMantissa
            );
    }

    /**
     * @notice Returns the current total borrows plus accrued interest
     * @return The total borrows with interest
     */
    function totalBorrowsCurrent()
        external
        override
        nonReentrant
        returns (uint256)
    {
        accrueInterest();
        return totalBorrows;
    }

    /**
     * @notice Accrue interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex
     * @param account The address whose balance should be calculated after updating borrowIndex
     * @return The calculated balance
     */
    function borrowBalanceCurrent(
        address account
    ) external override nonReentrant returns (uint256) {
        accrueInterest();
        return borrowBalanceStored(account);
    }

    /**
     * @notice Return the borrow balance of account based on stored data
     * @param account The address whose balance should be calculated
     * @return The calculated balance
     */
    function borrowBalanceStored(
        address account
    ) public view override returns (uint256) {
        return borrowBalanceStoredInternal(account);
    }

    /**
     * @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 borrowBalanceStoredInternal(
        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;
    }

    /**
     * @notice Accrue interest then return the up-to-date exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateCurrent()
        public
        override
        nonReentrant
        returns (uint256)
    {
        accrueInterest();
        return exchangeRateStored();
    }

    /**
     * @notice Calculates the exchange rate from the underlying to the PToken
     * @dev This function does not accrue interest before calculating the exchange rate
     * @return Calculated exchange rate scaled by 1e18
     */
    function exchangeRateStored() public view override returns (uint256) {
        return exchangeRateStoredInternal();
    }

    /**
     * @notice Calculates the exchange rate from the underlying to the PToken
     * @dev This function does not accrue interest before calculating the exchange rate
     * @return calculated exchange rate scaled by 1e18
     */
    function exchangeRateStoredInternal()
        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 Get cash balance of this pToken in the underlying asset
     * @return The quantity of underlying asset owned by this contract
     */
    function getCash() external view override returns (uint256) {
        return getCashPrior();
    }

    /**
     * @notice Applies accrued interest to total borrows and reserves
     * @dev This calculates interest accrued from the last checkpointed block
     *   up to the current block and writes new checkpoint to storage.
     */
    function accrueInterest() public virtual override returns (uint256) {
        /* Remember the initial block number */
        uint256 currentBlockNumber = getBlockNumber();
        uint256 accrualBlockNumberPrior = accrualBlockNumber;

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

        /* 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 = interestRateModel.getBorrowRate(
            cashPrior,
            borrowsPrior,
            reservesPrior
        );
        require(
            borrowRateMantissa <= borrowRateMaxMantissa,
            "borrow rate is absurdly high"
        );

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

        return NO_ERROR;
    }

    /**
     * @notice Sender supplies assets into the market and receives pTokens in exchange
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param mintAmount The amount of the underlying asset to supply
     */
    function mintInternal(uint256 mintAmount) internal nonReentrant {
        accrueInterest();
        // mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
        mintFresh(msg.sender, mintAmount);
    }

    /**
     * @notice User supplies assets into the market and receives pTokens 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
     */
    function mintFresh(address minter, uint256 mintAmount) internal {
        /* Fail if mint not allowed */
        uint256 allowed = peridottroller.mintAllowed(
            address(this),
            minter,
            mintAmount
        );
        if (allowed != 0) {
            revert MintPeridottrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert MintFreshnessCheck();
        }

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

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

        /*
         *  We call `doTransferIn` for the minter and the mintAmount.
         *  Note: The pToken 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 pToken holds an additional `actualMintAmount`
         *  of cash.
         */
        uint256 actualMintAmount = doTransferIn(minter, mintAmount);

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

        uint256 mintTokens = div_(actualMintAmount, exchangeRate);

        /*
         * We calculate the new total supply of pTokens 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 */
        // unused function
        // peridottroller.mintVerify(address(this), minter, actualMintAmount, mintTokens);
    }

    /**
     * @notice Sender redeems pTokens in exchange for the underlying asset
     * @dev Accrues interest whether or not the operation succeeds, unless reverted
     * @param redeemTokens The number of pTokens to redeem into underlying
     */
    function redeemInternal(uint256 redeemTokens) internal nonReentrant {
        accrueInterest();
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        redeemFresh(payable(msg.sender), redeemTokens, 0);
    }

    /**
     * @notice Sender redeems pTokens 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 receive from redeeming pTokens
     */
    function redeemUnderlyingInternal(
        uint256 redeemAmount
    ) internal nonReentrant {
        accrueInterest();
        // redeemFresh emits redeem-specific logs on errors, so we don't need to
        redeemFresh(payable(msg.sender), 0, redeemAmount);
    }

    /**
     * @notice User redeems pTokens in exchange for the underlying asset
     * @dev Assumes interest has already been accrued up to the current block
     * @param redeemer The address of the account which is redeeming the tokens
     * @param redeemTokensIn The number of pTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero)
     * @param redeemAmountIn The number of underlying tokens to receive from redeeming pTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero)
     */
    function redeemFresh(
        address payable redeemer,
        uint256 redeemTokensIn,
        uint256 redeemAmountIn
    ) internal {
        require(
            redeemTokensIn == 0 || redeemAmountIn == 0,
            "one of redeemTokensIn or redeemAmountIn must be zero"
        );

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

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

        /* Fail if redeem not allowed */
        uint256 allowed = peridottroller.redeemAllowed(
            address(this),
            redeemer,
            redeemTokens
        );
        if (allowed != 0) {
            revert RedeemPeridottrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert RedeemFreshnessCheck();
        }

        /* Fail gracefully if protocol has insufficient cash */
        if (getCashPrior() < redeemAmount) {
            revert RedeemTransferOutNotPossible();
        }

        /////////////////////////
        // 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 pToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the pToken has redeemAmount less of cash.
         *  doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
         */
        doTransferOut(redeemer, redeemAmount);

        /* We emit a Transfer event, and a Redeem event */
        emit Transfer(redeemer, address(this), redeemTokens);
        emit Redeem(redeemer, redeemAmount, redeemTokens);

        /* We call the defense hook */
        peridottroller.redeemVerify(
            address(this),
            redeemer,
            redeemAmount,
            redeemTokens
        );
    }

    /**
     * @notice Sender borrows assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     */
    function borrowInternal(uint256 borrowAmount) internal nonReentrant {
        accrueInterest();
        // borrowFresh emits borrow-specific logs on errors, so we don't need to
        borrowFresh(payable(msg.sender), borrowAmount);
    }

    /**
     * @notice Users borrow assets from the protocol to their own address
     * @param borrowAmount The amount of the underlying asset to borrow
     */
    function borrowFresh(
        address payable borrower,
        uint256 borrowAmount
    ) internal {
        /* Fail if borrow not allowed */
        uint256 allowed = peridottroller.borrowAllowed(
            address(this),
            borrower,
            borrowAmount
        );
        if (allowed != 0) {
            revert BorrowPeridottrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert BorrowFreshnessCheck();
        }

        /* Fail gracefully if protocol has insufficient underlying cash */
        if (getCashPrior() < borrowAmount) {
            revert BorrowCashNotAvailable();
        }

        /*
         * We calculate the new borrower and total borrow balances, failing on overflow:
         *  accountBorrowNew = accountBorrow + borrowAmount
         *  totalBorrowsNew = totalBorrows + borrowAmount
         */
        uint256 accountBorrowsPrev = borrowBalanceStoredInternal(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;

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

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

    /**
     * @notice Sender repays their own borrow
     * @param repayAmount The amount to repay, or -1 for the full outstanding amount
     */
    function repayBorrowInternal(uint256 repayAmount) internal nonReentrant {
        accrueInterest();
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        repayBorrowFresh(msg.sender, msg.sender, repayAmount);
    }

    /**
     * @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 repayBorrowBehalfInternal(
        address borrower,
        uint256 repayAmount
    ) internal nonReentrant {
        accrueInterest();
        // repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
        repayBorrowFresh(msg.sender, borrower, repayAmount);
    }

    /**
     * @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
     * @return (uint) the actual repayment amount.
     */
    function repayBorrowFresh(
        address payer,
        address borrower,
        uint256 repayAmount
    ) internal returns (uint256) {
        /* Fail if repayBorrow not allowed */
        uint256 allowed = peridottroller.repayBorrowAllowed(
            address(this),
            payer,
            borrower,
            repayAmount
        );
        if (allowed != 0) {
            revert RepayBorrowPeridottrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert RepayBorrowFreshnessCheck();
        }

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

        /* If repayAmount == -1, 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 pToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the pToken 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 = doTransferIn(payer, repayAmountFinal);

        /*
         * 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 The sender liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this pToken to be liquidated
     * @param pTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     */
    function liquidateBorrowInternal(
        address borrower,
        uint256 repayAmount,
        PTokenInterface pTokenCollateral
    ) internal nonReentrant {
        accrueInterest();

        uint256 error = pTokenCollateral.accrueInterest();
        if (error != NO_ERROR) {
            // accrueInterest emits logs on errors, but we still want to log the fact that an attempted liquidation failed
            revert LiquidateAccrueCollateralInterestFailed(error);
        }

        // liquidateBorrowFresh emits borrow-specific logs on errors, so we don't need to
        liquidateBorrowFresh(
            msg.sender,
            borrower,
            repayAmount,
            pTokenCollateral
        );
    }

    /**
     * @notice The liquidator liquidates the borrowers collateral.
     *  The collateral seized is transferred to the liquidator.
     * @param borrower The borrower of this pToken to be liquidated
     * @param liquidator The address repaying the borrow and seizing collateral
     * @param pTokenCollateral The market in which to seize collateral from the borrower
     * @param repayAmount The amount of the underlying borrowed asset to repay
     */
    function liquidateBorrowFresh(
        address liquidator,
        address borrower,
        uint256 repayAmount,
        PTokenInterface pTokenCollateral
    ) internal {
        /* Fail if liquidate not allowed */
        uint256 allowed = peridottroller.liquidateBorrowAllowed(
            address(this),
            address(pTokenCollateral),
            liquidator,
            borrower,
            repayAmount
        );
        if (allowed != 0) {
            revert LiquidatePeridottrollerRejection(allowed);
        }

        /* Verify market's block number equals current block number */
        if (accrualBlockNumber != getBlockNumber()) {
            revert LiquidateFreshnessCheck();
        }

        /* Verify pTokenCollateral market's block number equals current block number */
        if (pTokenCollateral.accrualBlockNumber() != getBlockNumber()) {
            revert LiquidateCollateralFreshnessCheck();
        }

        /* Fail if borrower = liquidator */
        if (borrower == liquidator) {
            revert LiquidateLiquidatorIsBorrower();
        }

        /* Fail if repayAmount = 0 */
        if (repayAmount == 0) {
            revert LiquidateCloseAmountIsZero();
        }

        /* Fail if repayAmount = -1 */
        if (repayAmount == type(uint256).max) {
            revert LiquidateCloseAmountIsUintMax();
        }

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

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

        /* We calculate the number of collateral tokens that will be seized */
        (uint256 amountSeizeError, uint256 seizeTokens) = peridottroller
            .liquidateCalculateSeizeTokens(
                address(this),
                address(pTokenCollateral),
                actualRepayAmount
            );
        require(
            amountSeizeError == NO_ERROR,
            "LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED"
        );

        /* Revert if borrower collateral token balance < seizeTokens */
        require(
            pTokenCollateral.balanceOf(borrower) >= seizeTokens,
            "LIQUIDATE_SEIZE_TOO_MUCH"
        );

        // If this is also the collateral, run seizeInternal to avoid re-entrancy, otherwise make an external call
        if (address(pTokenCollateral) == address(this)) {
            seizeInternal(address(this), liquidator, borrower, seizeTokens);
        } else {
            require(
                pTokenCollateral.seize(liquidator, borrower, seizeTokens) ==
                    NO_ERROR,
                "token seizure failed"
            );
        }

        /* We emit a LiquidateBorrow event */
        emit LiquidateBorrow(
            liquidator,
            borrower,
            actualRepayAmount,
            address(pTokenCollateral),
            seizeTokens
        );
    }

    /**
     * @notice Transfers collateral tokens (this market) to the liquidator.
     * @dev Will fail unless called by another pToken during the process of liquidation.
     *  Its absolutely critical to use msg.sender as the borrowed pToken and not a parameter.
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of pTokens to seize
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function seize(
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external override nonReentrant returns (uint256) {
        seizeInternal(msg.sender, liquidator, borrower, seizeTokens);

        return NO_ERROR;
    }

    /**
     * @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 PToken.
     *  Its absolutely critical to use msg.sender as the seizer pToken and not a parameter.
     * @param seizerToken The contract seizing the collateral (i.e. borrowed pToken)
     * @param liquidator The account receiving seized collateral
     * @param borrower The account having collateral seized
     * @param seizeTokens The number of pTokens to seize
     */
    function seizeInternal(
        address seizerToken,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) internal {
        /* Fail if seize not allowed */
        uint256 allowed = peridottroller.seizeAllowed(
            address(this),
            seizerToken,
            liquidator,
            borrower,
            seizeTokens
        );
        if (allowed != 0) {
            revert LiquidateSeizePeridottrollerRejection(allowed);
        }

        /* Fail if borrower = liquidator */
        if (borrower == liquidator) {
            revert LiquidateSeizeLiquidatorIsBorrower();
        }

        /*
         * 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: protocolSeizeShareMantissa})
        );
        uint256 liquidatorSeizeTokens = seizeTokens - protocolSeizeTokens;
        Exp memory exchangeRate = Exp({mantissa: exchangeRateStoredInternal()});
        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
        );
    }

    /**
     * Admin Functions **
     */

    /**
     * @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.
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setPendingAdmin(
        address payable newPendingAdmin
    ) external override returns (uint256) {
        // Check caller = admin
        if (msg.sender != admin) {
            revert SetPendingAdminOwnerCheck();
        }

        // Save current value, if any, for inclusion in log
        address oldPendingAdmin = pendingAdmin;

        // Store pendingAdmin with value newPendingAdmin
        pendingAdmin = newPendingAdmin;

        // Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
        emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);

        return NO_ERROR;
    }

    /**
     * @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
     * @dev Admin function for pending admin to accept role and update admin
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _acceptAdmin() external override returns (uint256) {
        // Check caller is pendingAdmin and pendingAdmin ≠ address(0)
        if (msg.sender != pendingAdmin || msg.sender == address(0)) {
            revert AcceptAdminPendingAdminCheck();
        }

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

        return NO_ERROR;
    }

    /**
     * @notice Sets a new peridottroller for the market
     * @dev Admin function to set a new peridottroller
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setPeridottroller(
        PeridottrollerInterface newPeridottroller
    ) public override returns (uint256) {
        // Check caller is admin
        if (msg.sender != admin) {
            revert SetPeridottrollerOwnerCheck();
        }

        PeridottrollerInterface oldPeridottroller = peridottroller;
        // Ensure invoke peridottroller.isPeridottroller() returns true
        require(
            newPeridottroller.isPeridottroller(),
            "marker method returned false"
        );

        // Set market's peridottroller to newPeridottroller
        peridottroller = newPeridottroller;

        // Emit NewPeridottroller(oldPeridottroller, newPeridottroller)
        emit NewPeridottroller(oldPeridottroller, newPeridottroller);

        return NO_ERROR;
    }

    /**
     * @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
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setReserveFactor(
        uint256 newReserveFactorMantissa
    ) external override nonReentrant returns (uint256) {
        accrueInterest();
        // _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
        return _setReserveFactorFresh(newReserveFactorMantissa);
    }

    /**
     * @notice Sets a new reserve factor for the protocol (*requires fresh interest accrual)
     * @dev Admin function to set a new reserve factor
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setReserveFactorFresh(
        uint256 newReserveFactorMantissa
    ) internal returns (uint256) {
        // Check caller is admin
        if (msg.sender != admin) {
            revert SetReserveFactorAdminCheck();
        }

        // Verify market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            revert SetReserveFactorFreshCheck();
        }

        // Check newReserveFactor ≤ maxReserveFactor
        if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
            revert SetReserveFactorBoundsCheck();
        }

        uint256 oldReserveFactorMantissa = reserveFactorMantissa;
        reserveFactorMantissa = newReserveFactorMantissa;

        emit NewReserveFactor(
            oldReserveFactorMantissa,
            newReserveFactorMantissa
        );

        return NO_ERROR;
    }

    /**
     * @notice Accrues interest and reduces reserves by transferring from msg.sender
     * @param addAmount Amount of addition to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _addReservesInternal(
        uint256 addAmount
    ) internal nonReentrant returns (uint256) {
        accrueInterest();

        // _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
        _addReservesFresh(addAmount);
        return NO_ERROR;
    }

    /**
     * @notice Add reserves by transferring from caller
     * @dev Requires fresh interest accrual
     * @param addAmount Amount of addition to reserves
     * @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees
     */
    function _addReservesFresh(
        uint256 addAmount
    ) internal returns (uint256, uint256) {
        // totalReserves + actualAddAmount
        uint256 totalReservesNew;
        uint256 actualAddAmount;

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            revert AddReservesFactorFreshCheck(actualAddAmount);
        }

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

        /*
         * We call doTransferIn for the caller and the addAmount
         *  Note: The pToken must handle variations between ERC-20 and ETH underlying.
         *  On success, the pToken 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);

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

        /* Return (NO_ERROR, actualAddAmount) */
        return (NO_ERROR, actualAddAmount);
    }

    /**
     * @notice Accrues interest and reduces reserves by transferring to admin
     * @param reduceAmount Amount of reduction to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _reduceReserves(
        uint256 reduceAmount
    ) external override nonReentrant returns (uint256) {
        accrueInterest();
        // _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
        return _reduceReservesFresh(reduceAmount);
    }

    /**
     * @notice Reduces reserves by transferring to admin
     * @dev Requires fresh interest accrual
     * @param reduceAmount Amount of reduction to reserves
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _reduceReservesFresh(
        uint256 reduceAmount
    ) internal returns (uint256) {
        // totalReserves - reduceAmount
        uint256 totalReservesNew;

        // Check caller is admin
        if (msg.sender != admin) {
            revert ReduceReservesAdminCheck();
        }

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            revert ReduceReservesFreshCheck();
        }

        // Fail gracefully if protocol has insufficient underlying cash
        if (getCashPrior() < reduceAmount) {
            revert ReduceReservesCashNotAvailable();
        }

        // Check reduceAmount ≤ reserves[n] (totalReserves)
        if (reduceAmount > totalReserves) {
            revert ReduceReservesCashValidation();
        }

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

        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(admin, reduceAmount);

        emit ReservesReduced(admin, reduceAmount, totalReservesNew);

        return NO_ERROR;
    }

    /**
     * @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
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setInterestRateModel(
        InterestRateModel newInterestRateModel
    ) public override returns (uint256) {
        accrueInterest();
        // _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
        return _setInterestRateModelFresh(newInterestRateModel);
    }

    /**
     * @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
     * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
     */
    function _setInterestRateModelFresh(
        InterestRateModel newInterestRateModel
    ) internal returns (uint256) {
        // Used to store old model for use in the event that is emitted on success
        InterestRateModel oldInterestRateModel;

        // Check caller is admin
        if (msg.sender != admin) {
            revert SetInterestRateModelOwnerCheck();
        }

        // We fail gracefully unless market's block number equals current block number
        if (accrualBlockNumber != getBlockNumber()) {
            revert SetInterestRateModelFreshCheck();
        }

        // Track the market's current interest rate model
        oldInterestRateModel = interestRateModel;

        // Ensure invoke newInterestRateModel.isInterestRateModel() returns true
        require(
            newInterestRateModel.isInterestRateModel(),
            "marker method returned false"
        );

        // Set the interest rate model to newInterestRateModel
        interestRateModel = newInterestRateModel;

        // Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
        emit NewMarketInterestRateModel(
            oldInterestRateModel,
            newInterestRateModel
        );

        return NO_ERROR;
    }

    /**
     * Flash Loan Functions **
     */

    /**
     * @notice Returns the maximum amount available for flash loan for a given token
     * @param token The address of the token to flash loan
     * @return The maximum amount available for flash loan
     */
    function maxFlashLoan(
        address token
    )
        external
        view
        override(PTokenInterface, IERC3156FlashLender)
        returns (uint256)
    {
        // Only allow flash loans of the underlying token
        if (token != getUnderlyingAddress()) {
            return 0;
        }

        // If flash loans are paused, return 0
        if (flashLoansPaused) {
            return 0;
        }

        uint256 cash = getCashPrior();
        uint256 maxAmount = (cash * maxFlashLoanRatio) / 10000;

        return maxAmount;
    }

    /**
     * @notice Returns the fee for a flash loan of the given amount
     * @param token The address of the token to flash loan
     * @param amount The amount to flash loan
     * @return The fee for the flash loan
     */
    function flashFee(
        address token,
        uint256 amount
    )
        external
        view
        override(PTokenInterface, IERC3156FlashLender)
        returns (uint256)
    {
        // Only allow flash loans of the underlying token
        require(
            token == getUnderlyingAddress(),
            "FlashLoan: token not supported"
        );

        return (amount * flashLoanFeeBps) / 10000;
    }

    /**
     * @notice Executes a flash loan
     * @param receiver The contract that will receive the flash loan
     * @param token The address of the token to flash loan
     * @param amount The amount to flash loan
     * @param data Arbitrary data to pass to the receiver
     * @return True if the flash loan was successful
     */
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    )
        external
        override(PTokenInterface, IERC3156FlashLender)
        nonReentrant
        returns (bool)
    {
        // Only allow flash loans of the underlying token
        require(
            token == getUnderlyingAddress(),
            "FlashLoan: token not supported"
        );

        // Check if flash loans are paused
        require(!flashLoansPaused, "FlashLoan: flash loans are paused");

        // Check if amount is within limits
        require(
            amount <= this.maxFlashLoan(token),
            "FlashLoan: amount exceeds maximum"
        );

        // Calculate minimum required fee
        uint256 minFee = (amount * flashLoanFeeBps) / 10000;

        // Get cash balance before
        uint256 balanceBefore = getCashPrior();

        // Transfer tokens to receiver
        doTransferOut(payable(address(receiver)), amount);

        // Execute the flash loan callback
        require(
            receiver.onFlashLoan(msg.sender, token, amount, minFee, data) ==
                keccak256("ERC3156FlashBorrower.onFlashLoan"),
            "FlashLoan: invalid callback return"
        );

        // Get cash balance after
        uint256 balanceAfter = getCashPrior();

        // Compute the actual fee paid as the delta of cash
        require(balanceAfter >= balanceBefore, "FlashLoan: loan not repaid");
        uint256 actualFee = balanceAfter - balanceBefore;

        // Require borrower paid at least the minimum fee
        require(actualFee >= minFee, "FlashLoan: loan not repaid");

        // Update reserves with the actual fee to avoid inflating available cash
        if (actualFee > 0) {
            totalReserves = totalReserves + actualFee;
        }

        // Emit flash loan event with the actual fee
        emit FlashLoan(address(receiver), token, amount, actualFee);

        return true;
    }

    /**
     * @notice Admin function to set flash loan fee
     * @param newFeeBps New fee in basis points (1 = 0.01%)
     */
    function _setFlashLoanFee(uint256 newFeeBps) external returns (uint256) {
        // Check caller is admin
        require(msg.sender == admin, "Only admin can set flash loan fee");

        // Fee cannot exceed 1% (100 basis points)
        require(newFeeBps <= 100, "Flash loan fee too high");

        uint256 oldFeeBps = flashLoanFeeBps;
        flashLoanFeeBps = newFeeBps;

        emit NewFlashLoanFee(oldFeeBps, newFeeBps);

        return NO_ERROR;
    }

    /**
     * @notice Admin function to set maximum flash loan ratio
     * @param newMaxRatio New maximum ratio in basis points (10000 = 100%)
     */
    function _setMaxFlashLoanRatio(
        uint256 newMaxRatio
    ) external returns (uint256) {
        // Check caller is admin
        require(msg.sender == admin, "Only admin can set max flash loan ratio");

        // Ratio cannot exceed 100% (10000 basis points)
        require(newMaxRatio <= 10000, "Flash loan ratio too high");

        uint256 oldMaxRatio = maxFlashLoanRatio;
        maxFlashLoanRatio = newMaxRatio;

        emit NewMaxFlashLoanRatio(oldMaxRatio, newMaxRatio);

        return NO_ERROR;
    }

    /**
     * @notice Admin function to pause/unpause flash loans
     * @param state True to pause, false to unpause
     */
    function _setFlashLoansPaused(bool state) external returns (bool) {
        // Check caller is admin or pause guardian
        if (
            !(msg.sender == admin ||
                msg.sender ==
                PeridottrollerV2Storage(address(peridottroller))
                    .pauseGuardian())
        ) {
            return false;
        }

        flashLoansPaused = state;
        emit FlashLoansPaused(state);

        return true;
    }

    /**
     * @notice Get the underlying token address (to be implemented in derived contracts)
     * @return The address of the underlying token
     */
    function getUnderlyingAddress() internal view virtual returns (address);

    /**
     * Safe Token **
     */

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

    /**
     * Reentrancy Guard **
     */

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     */
    modifier nonReentrant() {
        require(_notEntered, "re-entered");
        _notEntered = false;
        _;
        _notEntered = true; // get a gas-refund post-Istanbul
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

File 10 of 19 : PTokenInterfaces.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PeridottrollerInterface.sol";
import "./InterestRateModel.sol";
import "./EIP20NonStandardInterface.sol";
import "./ErrorReporter.sol";

// Add ERC-3156 imports
interface IERC3156FlashBorrower {
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external returns (bytes32);
}

interface IERC3156FlashLender {
    function maxFlashLoan(address token) external view returns (uint256);

    function flashFee(
        address token,
        uint256 amount
    ) external view returns (uint256);

    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bool);
}

contract PTokenStorage {
    /**
     * @dev Guard variable for re-entrancy checks
     */
    bool internal _notEntered;

    /**
     * @notice EIP-20 token name for this token
     */
    string public name;

    /**
     * @notice EIP-20 token symbol for this token
     */
    string public symbol;

    /**
     * @notice EIP-20 token decimals for this token
     */
    uint8 public decimals;

    // Maximum borrow rate that can ever be applied (.0005% / block)
    uint256 internal constant borrowRateMaxMantissa = 0.0005e16;

    // Maximum fraction of interest that can be set aside for reserves
    uint256 internal constant reserveFactorMaxMantissa = 1e18;

    /**
     * @notice Administrator for this contract
     */
    address payable public admin;

    /**
     * @notice Pending administrator for this contract
     */
    address payable public pendingAdmin;

    /**
     * @notice Contract which oversees inter-pToken operations
     */
    PeridottrollerInterface public peridottroller;

    /**
     * @notice Model which tells what the current interest rate should be
     */
    InterestRateModel public interestRateModel;

    // Initial exchange rate used when minting the first PTokens (used when totalSupply = 0)
    uint256 internal initialExchangeRateMantissa;

    /**
     * @notice Fraction of interest currently set aside for reserves
     */
    uint256 public reserveFactorMantissa;

    /**
     * @notice Block number that interest was last accrued at
     */
    uint256 public accrualBlockNumber;

    /**
     * @notice Accumulator of the total earned interest rate since the opening of the market
     */
    uint256 public borrowIndex;

    /**
     * @notice Total amount of outstanding borrows of the underlying in this market
     */
    uint256 public totalBorrows;

    /**
     * @notice Total amount of reserves of the underlying held in this market
     */
    uint256 public totalReserves;

    /**
     * @notice Total number of tokens in circulation
     */
    uint256 public totalSupply;

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

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

    /**
     * Flash Loan Storage **
     */

    /**
     * @notice Flash loan fee rate (in basis points). 1 = 0.01%
     */
    uint256 public flashLoanFeeBps;

    /**
     * @notice Maximum flash loan amount as a percentage of total cash (in basis points)
     * 10000 = 100% of available cash
     */
    uint256 public maxFlashLoanRatio;

    /**
     * @notice Whether flash loans are paused
     */
    bool public flashLoansPaused;
}

abstract contract PTokenInterface is PTokenStorage {
    /**
     * @notice Indicator that this is a PToken contract (for inspection)
     */
    bool public constant isPToken = true;

    /**
     * Market 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 minter, uint256 mintAmount, uint256 mintTokens);

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

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

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

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

    /**
     * Admin Events **
     */

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

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

    /**
     * @notice Event emitted when peridottroller is changed
     */
    event NewPeridottroller(
        PeridottrollerInterface oldPeridottroller,
        PeridottrollerInterface newPeridottroller
    );

    /**
     * @notice Event emitted when interestRateModel is changed
     */
    event NewMarketInterestRateModel(
        InterestRateModel oldInterestRateModel,
        InterestRateModel 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 benefactor,
        uint256 addAmount,
        uint256 newTotalReserves
    );

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

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

    /**
     * Flash Loan Events **
     */

    /**
     * @notice Event emitted when a flash loan is executed
     */
    event FlashLoan(
        address indexed receiver,
        address indexed token,
        uint256 amount,
        uint256 fee
    );

    /**
     * @notice Event emitted when flash loan fee is changed
     */
    event NewFlashLoanFee(uint256 oldFeeBps, uint256 newFeeBps);

    /**
     * @notice Event emitted when maximum flash loan ratio is changed
     */
    event NewMaxFlashLoanRatio(uint256 oldMaxRatio, uint256 newMaxRatio);

    /**
     * @notice Event emitted when flash loans are paused/unpaused
     */
    event FlashLoansPaused(bool paused);

    /**
     * User Interface **
     */
    function transfer(
        address dst,
        uint256 amount
    ) external virtual returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external virtual returns (bool);

    function approve(
        address spender,
        uint256 amount
    ) external virtual returns (bool);

    function allowance(
        address owner,
        address spender
    ) external view virtual returns (uint256);

    function balanceOf(address owner) external view virtual returns (uint256);

    function balanceOfUnderlying(
        address owner
    ) external virtual returns (uint256);

    function getAccountSnapshot(
        address account
    ) external view virtual returns (uint256, uint256, uint256, uint256);

    function borrowRatePerBlock() external view virtual returns (uint256);

    function supplyRatePerBlock() external view virtual returns (uint256);

    function totalBorrowsCurrent() external virtual returns (uint256);

    function borrowBalanceCurrent(
        address account
    ) external virtual returns (uint256);

    function borrowBalanceStored(
        address account
    ) external view virtual returns (uint256);

    function exchangeRateCurrent() external virtual returns (uint256);

    function exchangeRateStored() external view virtual returns (uint256);

    function getCash() external view virtual returns (uint256);

    function accrueInterest() external virtual returns (uint256);

    function seize(
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external virtual returns (uint256);

    /**
     * Admin Functions **
     */
    function _setPendingAdmin(
        address payable newPendingAdmin
    ) external virtual returns (uint256);

    function _acceptAdmin() external virtual returns (uint256);

    function _setPeridottroller(
        PeridottrollerInterface newPeridottroller
    ) external virtual returns (uint256);

    function _setReserveFactor(
        uint256 newReserveFactorMantissa
    ) external virtual returns (uint256);

    function _reduceReserves(
        uint256 reduceAmount
    ) external virtual returns (uint256);

    function _setInterestRateModel(
        InterestRateModel newInterestRateModel
    ) external virtual returns (uint256);

    /**
     * Flash Loan Functions **
     */
    function maxFlashLoan(
        address token
    ) external view virtual returns (uint256);

    function flashFee(
        address token,
        uint256 amount
    ) external view virtual returns (uint256);

    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external virtual returns (bool);
}

contract PErc20Storage {
    /**
     * @notice Underlying asset for this PToken
     */
    address public underlying;
}

abstract contract PErc20Interface is PErc20Storage {
    /**
     * User Interface **
     */
    function mint(uint256 mintAmount) external virtual returns (uint256);

    function redeem(uint256 redeemTokens) external virtual returns (uint256);

    function redeemUnderlying(
        uint256 redeemAmount
    ) external virtual returns (uint256);

    function borrow(uint256 borrowAmount) external virtual returns (uint256);

    function repayBorrow(
        uint256 repayAmount
    ) external virtual returns (uint256);

    function repayBorrowBehalf(
        address borrower,
        uint256 repayAmount
    ) external virtual returns (uint256);

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        PTokenInterface pTokenCollateral
    ) external virtual returns (uint256);

    function sweepToken(EIP20NonStandardInterface token) external virtual;

    /**
     * Admin Functions **
     */
    function _addReserves(uint256 addAmount) external virtual returns (uint256);
}

contract CDelegationStorage {
    /**
     * @notice Implementation address for this contract
     */
    address public implementation;
}

abstract contract CDelegatorInterface is CDelegationStorage {
    /**
     * @notice Emitted when implementation is changed
     */
    event NewImplementation(
        address oldImplementation,
        address newImplementation
    );

    /**
     * @notice Called by the admin to update the implementation of the delegator
     * @param implementation_ The address of the new implementation for delegation
     * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation
     * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
     */
    function _setImplementation(
        address implementation_,
        bool allowResign,
        bytes memory becomeImplementationData
    ) external virtual;
}

abstract contract CDelegateInterface is CDelegationStorage {
    /**
     * @notice Called by the delegator on a delegate to initialize it for duty
     * @dev Should revert if any issues arise which make it unfit for delegation
     * @param data The encoded bytes data for any initialization
     */
    function _becomeImplementation(bytes memory data) external virtual;

    /**
     * @notice Called by the delegator on a delegate to forfeit its responsibility
     */
    function _resignImplementation() external virtual;
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

contract PeridottrollerErrorReporter {
    enum Error {
        NO_ERROR,
        UNAUTHORIZED,
        COMPTROLLER_MISMATCH,
        INSUFFICIENT_SHORTFALL,
        INSUFFICIENT_LIQUIDITY,
        INVALID_CLOSE_FACTOR,
        INVALID_COLLATERAL_FACTOR,
        INVALID_LIQUIDATION_INCENTIVE,
        MARKET_NOT_ENTERED, // no longer possible
        MARKET_NOT_LISTED,
        MARKET_ALREADY_LISTED,
        MATH_ERROR,
        NONZERO_BORROW_BALANCE,
        PRICE_ERROR,
        REJECTION,
        SNAPSHOT_ERROR,
        TOO_MANY_ASSETS,
        TOO_MUCH_REPAY
    }

    enum FailureInfo {
        ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
        ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
        EXIT_MARKET_BALANCE_OWED,
        EXIT_MARKET_REJECTION,
        SET_CLOSE_FACTOR_OWNER_CHECK,
        SET_CLOSE_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_OWNER_CHECK,
        SET_COLLATERAL_FACTOR_NO_EXISTS,
        SET_COLLATERAL_FACTOR_VALIDATION,
        SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
        SET_IMPLEMENTATION_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
        SET_LIQUIDATION_INCENTIVE_VALIDATION,
        SET_MAX_ASSETS_OWNER_CHECK,
        SET_PENDING_ADMIN_OWNER_CHECK,
        SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
        SET_PRICE_ORACLE_OWNER_CHECK,
        SUPPORT_MARKET_EXISTS,
        SUPPORT_MARKET_OWNER_CHECK,
        SET_PAUSE_GUARDIAN_OWNER_CHECK,
        SET_FLASH_LOAN_FEE_OWNER_CHECK,
        SET_FLASH_LOAN_FEE_VALIDATION,
        SET_MAX_FLASH_LOAN_RATIO_OWNER_CHECK,
        SET_MAX_FLASH_LOAN_RATIO_VALIDATION
    }

    /**
     * @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
     * contract-specific code that enables us to report opaque error codes from upgradeable contracts.
     *
     */
    event Failure(uint256 error, uint256 info, uint256 detail);

    /**
     * @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
     */
    function fail(Error err, FailureInfo info) internal returns (uint256) {
        emit Failure(uint256(err), uint256(info), 0);

        return uint256(err);
    }

    /**
     * @dev use this when reporting an opaque error from an upgradeable collaborator contract
     */
    function failOpaque(Error err, FailureInfo info, uint256 opaqueError) internal returns (uint256) {
        emit Failure(uint256(err), uint256(info), opaqueError);

        return uint256(err);
    }
}

contract TokenErrorReporter {
    uint256 public constant NO_ERROR = 0; // support legacy return codes

    error TransferPeridottrollerRejection(uint256 errorCode);
    error TransferNotAllowed();
    error TransferNotEnough();
    error TransferTooMuch();

    error MintPeridottrollerRejection(uint256 errorCode);
    error MintFreshnessCheck();

    error RedeemPeridottrollerRejection(uint256 errorCode);
    error RedeemFreshnessCheck();
    error RedeemTransferOutNotPossible();

    error BorrowPeridottrollerRejection(uint256 errorCode);
    error BorrowFreshnessCheck();
    error BorrowCashNotAvailable();

    error RepayBorrowPeridottrollerRejection(uint256 errorCode);
    error RepayBorrowFreshnessCheck();

    error LiquidatePeridottrollerRejection(uint256 errorCode);
    error LiquidateFreshnessCheck();
    error LiquidateCollateralFreshnessCheck();
    error LiquidateAccrueBorrowInterestFailed(uint256 errorCode);
    error LiquidateAccrueCollateralInterestFailed(uint256 errorCode);
    error LiquidateLiquidatorIsBorrower();
    error LiquidateCloseAmountIsZero();
    error LiquidateCloseAmountIsUintMax();
    error LiquidateRepayBorrowFreshFailed(uint256 errorCode);

    error LiquidateSeizePeridottrollerRejection(uint256 errorCode);
    error LiquidateSeizeLiquidatorIsBorrower();

    error AcceptAdminPendingAdminCheck();

    error SetPeridottrollerOwnerCheck();
    error SetPendingAdminOwnerCheck();

    error SetReserveFactorAdminCheck();
    error SetReserveFactorFreshCheck();
    error SetReserveFactorBoundsCheck();

    error AddReservesFactorFreshCheck(uint256 actualAddAmount);

    error ReduceReservesAdminCheck();
    error ReduceReservesFreshCheck();
    error ReduceReservesCashNotAvailable();
    error ReduceReservesCashValidation();

    error SetInterestRateModelOwnerCheck();
    error SetInterestRateModelFreshCheck();
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

/**
 * @title ERC 20 Token Standard Interface
 *  https://eips.ethereum.org/EIPS/eip-20
 */
interface EIP20Interface {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);

    /**
     * @notice Get the total number of tokens in circulation
     * @return The supply of tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return balance The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @notice Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return success Whether or not the transfer succeeded
     */
    function transfer(address dst, uint256 amount) external returns (bool success);

    /**
     * @notice Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     * @return success Whether or not the transfer succeeded
     */
    function transferFrom(address src, address dst, uint256 amount) external returns (bool success);

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved (-1 means infinite)
     * @return success Whether or not the approval succeeded
     */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return remaining The number of tokens allowed to be spent (-1 means infinite)
     */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

File 13 of 19 : ExponentialNoError.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

/**
 * @title Exponential module for storing fixed-precision decimals
 * @author Peridot
 * @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})`.
 */
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)});
    }
}

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PToken.sol";
import "./PriceOracle.sol";

contract UnitrollerAdminStorage {
    /**
     * @notice Administrator for this contract
     */
    address public admin;

    /**
     * @notice Pending administrator for this contract
     */
    address public pendingAdmin;

    /**
     * @notice Active brains of Unitroller
     */
    address public peridottrollerImplementation;

    /**
     * @notice Pending brains of Unitroller
     */
    address public pendingPeridottrollerImplementation;
}

contract PeridottrollerV1Storage is UnitrollerAdminStorage {
    /**
     * @notice Oracle which gives the price of any given asset
     */
    PriceOracle public oracle;

    /**
     * @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
     */
    uint256 public closeFactorMantissa;

    /**
     * @notice Multiplier representing the discount on collateral that a liquidator receives
     */
    uint256 public liquidationIncentiveMantissa;

    /**
     * @notice Max number of assets a single account can participate in (borrow or use as collateral)
     */
    uint256 public maxAssets;

    /**
     * @notice Per-account mapping of "assets you are in", capped by maxAssets
     */
    mapping(address => PToken[]) public accountAssets;
}

contract PeridottrollerV2Storage is PeridottrollerV1Storage {
    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 PERIDOT
        bool isPeridoted;
    }

    /**
     * @notice Official mapping of pTokens -> Market metadata
     * @dev Used e.g. to determine if a market is supported
     */
    mapping(address => Market) public markets;

    /**
     * @notice The Pause Guardian can pause certain actions as a safety mechanism.
     *  Actions which allow users to remove their own assets cannot be paused.
     *  Liquidation / seizing / transfer can only be paused globally, not by market.
     */
    address public pauseGuardian;
    bool public _mintGuardianPaused;
    bool public _borrowGuardianPaused;
    bool public transferGuardianPaused;
    bool public seizeGuardianPaused;
    mapping(address => bool) public mintGuardianPaused;
    mapping(address => bool) public borrowGuardianPaused;
}

contract PeridottrollerV3Storage is PeridottrollerV2Storage {
    struct PeridotMarketState {
        // The market's last updated peridotBorrowIndex or peridotSupplyIndex
        uint224 index;
        // The block number the index was last updated at
        uint32 block;
    }

    /// @notice A list of all markets
    PToken[] public allMarkets;

    /// @notice The rate at which the flywheel distributes PERIDOT, per block
    uint256 public peridotRate;

    /// @notice The portion of peridotRate that each market currently receives
    mapping(address => uint256) public peridotSpeeds;

    /// @notice The PERIDOT market supply state for each market
    mapping(address => PeridotMarketState) public peridotSupplyState;

    /// @notice The PERIDOT market borrow state for each market
    mapping(address => PeridotMarketState) public peridotBorrowState;

    /// @notice The PERIDOT borrow index for each market for each supplier as of the last time they accrued PERIDOT
    mapping(address => mapping(address => uint256)) public peridotSupplierIndex;

    /// @notice The PERIDOT borrow index for each market for each borrower as of the last time they accrued PERIDOT
    mapping(address => mapping(address => uint256)) public peridotBorrowerIndex;

    /// @notice The PERIDOT accrued but not yet transferred to each user
    mapping(address => uint256) public peridotAccrued;
}

contract PeridottrollerV4Storage is PeridottrollerV3Storage {
    // @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market.
    address public borrowCapGuardian;

    // @notice Borrow caps enforced by borrowAllowed for each pToken address. Defaults to zero which corresponds to unlimited borrowing.
    mapping(address => uint256) public borrowCaps;
}

contract PeridottrollerV5Storage is PeridottrollerV4Storage {
    /// @notice The portion of PERIDOT that each contributor receives per block
    mapping(address => uint256) public peridotContributorSpeeds;

    /// @notice Last block at which a contributor's PERIDOT rewards have been allocated
    mapping(address => uint256) public lastContributorBlock;
}

contract PeridottrollerV6Storage is PeridottrollerV5Storage {
    /// @notice The rate at which peridot is distributed to the corresponding borrow market (per block)
    mapping(address => uint256) public peridotBorrowSpeeds;

    /// @notice The rate at which peridot is distributed to the corresponding supply market (per block)
    mapping(address => uint256) public peridotSupplySpeeds;
}

contract PeridottrollerV7Storage is PeridottrollerV6Storage {
    /// @notice Flag indicating whether the function to fix PERIDOT accruals has been executed (RE: proposal 62 bug)
    bool public proposal65FixExecuted;

    /// @notice Accounting storage mapping account addresses to how much PERIDOT they owe the protocol.
    mapping(address => uint256) public peridotReceivable;
}

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

/**
 * @title EIP20NonStandardInterface
 * @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
 *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */
interface EIP20NonStandardInterface {
    /**
     * @notice Get the total number of tokens in circulation
     * @return The supply of tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return balance The balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
     * @notice Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     */
    function transfer(address dst, uint256 amount) external;

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
     * @notice Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     */
    function transferFrom(address src, address dst, uint256 amount) external;

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved
     * @return success Whether or not the approval succeeded
     */
    function approve(address spender, uint256 amount) external returns (bool success);

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return remaining The number of tokens allowed to be spent
     */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}

File 18 of 19 : PriceOracle.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;

import "./PToken.sol";

abstract contract PriceOracle {
    /// @notice Indicator that this is a PriceOracle contract (for inspection)
    bool public constant isPriceOracle = true;

    /**
     * @notice Get the underlying price of a pToken asset
     * @param pToken The pToken to get the underlying price of
     * @return The underlying asset price mantissa (scaled by 1e18).
     *  Zero means the price is unavailable.
     */
    function getUnderlyingPrice(
        PToken pToken
    ) external view virtual returns (uint256);
}

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

pragma solidity ^0.8.20;

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

Settings
{
  "remappings": [
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@chainlink/contracts-ccip/contracts/=node_modules/@chainlink/contracts-ccip/contracts/",
    "@chainlink/contracts/=node_modules/@chainlink/contracts/",
    "solidity-bytes-utils/=lib/solidity-bytes-utils/",
    "@arbitrum/=node_modules/@arbitrum/",
    "@axelar-network/=node_modules/@axelar-network/",
    "@eth-optimism/=node_modules/@eth-optimism/",
    "@offchainlabs/=node_modules/@offchainlabs/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@pythnetwork/=node_modules/@pythnetwork/",
    "@scroll-tech/=node_modules/@scroll-tech/",
    "@storknetwork/=node_modules/@storknetwork/",
    "@thirdweb-dev/=node_modules/@thirdweb-dev/",
    "@zksync/=node_modules/@zksync/",
    "account-abstraction/=lib/account-abstraction/contracts/",
    "createx/=lib/pyth-crosschain/lazer/contracts/evm/lib/createx/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "erc721a-upgradeable/=node_modules/erc721a-upgradeable/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "hardhat/=node_modules/hardhat/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/pyth-crosschain/target_chains/ethereum/sdk/stylus/pyth-mock-solidity/lib/openzeppelin-foundry-upgrades/src/",
    "pyth-crosschain/=lib/pyth-crosschain/",
    "solady/=node_modules/solady/",
    "solidity-stringutils/=lib/pyth-crosschain/target_chains/ethereum/sdk/stylus/pyth-mock-solidity/lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
    "sstore2/=node_modules/@thirdweb-dev/dynamic-contracts/lib/sstore2/",
    "wormhole-ntt/=lib/wormhole-ntt/src/",
    "wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": true,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"AcceptAdminPendingAdminCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualAddAmount","type":"uint256"}],"name":"AddReservesFactorFreshCheck","type":"error"},{"inputs":[],"name":"BorrowCashNotAvailable","type":"error"},{"inputs":[],"name":"BorrowFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"BorrowPeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueBorrowInterestFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateAccrueCollateralInterestFailed","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsUintMax","type":"error"},{"inputs":[],"name":"LiquidateCloseAmountIsZero","type":"error"},{"inputs":[],"name":"LiquidateCollateralFreshnessCheck","type":"error"},{"inputs":[],"name":"LiquidateFreshnessCheck","type":"error"},{"inputs":[],"name":"LiquidateLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidatePeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateRepayBorrowFreshFailed","type":"error"},{"inputs":[],"name":"LiquidateSeizeLiquidatorIsBorrower","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"LiquidateSeizePeridottrollerRejection","type":"error"},{"inputs":[],"name":"MintFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"MintPeridottrollerRejection","type":"error"},{"inputs":[],"name":"RedeemFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RedeemPeridottrollerRejection","type":"error"},{"inputs":[],"name":"RedeemTransferOutNotPossible","type":"error"},{"inputs":[],"name":"ReduceReservesAdminCheck","type":"error"},{"inputs":[],"name":"ReduceReservesCashNotAvailable","type":"error"},{"inputs":[],"name":"ReduceReservesCashValidation","type":"error"},{"inputs":[],"name":"ReduceReservesFreshCheck","type":"error"},{"inputs":[],"name":"RepayBorrowFreshnessCheck","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"RepayBorrowPeridottrollerRejection","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SetInterestRateModelFreshCheck","type":"error"},{"inputs":[],"name":"SetInterestRateModelOwnerCheck","type":"error"},{"inputs":[],"name":"SetPendingAdminOwnerCheck","type":"error"},{"inputs":[],"name":"SetPeridottrollerOwnerCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorAdminCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorBoundsCheck","type":"error"},{"inputs":[],"name":"SetReserveFactorFreshCheck","type":"error"},{"inputs":[],"name":"TransferNotAllowed","type":"error"},{"inputs":[],"name":"TransferNotEnough","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorCode","type":"uint256"}],"name":"TransferPeridottrollerRejection","type":"error"},{"inputs":[],"name":"TransferTooMuch","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":"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":false,"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":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"FlashLoansPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"liquidator","type":"address"},{"indexed":false,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"pTokenCollateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"seizeTokens","type":"uint256"}],"name":"LiquidateBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"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":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"NewAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldFeeBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"NewFlashLoanFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract InterestRateModel","name":"oldInterestRateModel","type":"address"},{"indexed":false,"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"NewMarketInterestRateModel","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMaxRatio","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMaxRatio","type":"uint256"}],"name":"NewMaxFlashLoanRatio","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldPendingAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newPendingAdmin","type":"address"}],"name":"NewPendingAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract PeridottrollerInterface","name":"oldPeridottroller","type":"address"},{"indexed":false,"internalType":"contract PeridottrollerInterface","name":"newPeridottroller","type":"address"}],"name":"NewPeridottroller","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":false,"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":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedemptionCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"RedemptionRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newReferralId","type":"uint256"}],"name":"ReferralIdUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"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":false,"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":false,"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":false,"internalType":"uint256","name":"previousMantissa","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMantissa","type":"uint256"}],"name":"VaultBufferUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"VaultDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"VaultPausedUpdated","type":"event"},{"inputs":[],"name":"NO_ERROR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_acceptAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"addAmount","type":"uint256"}],"name":"_addReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"_becomeImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_completeRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"peridotLikeDelegatee","type":"address"}],"name":"_delegatePeridotLikeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reduceAmount","type":"uint256"}],"name":"_reduceReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"_requestRedemption","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_resignImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"_setFlashLoanFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"_setFlashLoansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract InterestRateModel","name":"newInterestRateModel","type":"address"}],"name":"_setInterestRateModel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault_","type":"address"},{"internalType":"uint256","name":"buffer_","type":"uint256"}],"name":"_setMagmaVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMaxRatio","type":"uint256"}],"name":"_setMaxFlashLoanRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingAdmin","type":"address"}],"name":"_setPendingAdmin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PeridottrollerInterface","name":"newPeridottroller","type":"address"}],"name":"_setPeridottroller","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReferralId","type":"uint256"}],"name":"_setReferralId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactorMantissa","type":"uint256"}],"name":"_setReserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMantissa","type":"uint256"}],"name":"_setVaultBuffer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"pause","type":"bool"}],"name":"_setVaultPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrualBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"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":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[],"name":"borrowRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCompleteRedemption","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","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":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flashLoanFeeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flashLoansPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"underlying_","type":"address"},{"internalType":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","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":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","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":"contract PeridottrollerInterface","name":"peridottroller_","type":"address"},{"internalType":"contract InterestRateModel","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":"contract IMagma","name":"magmaVault_","type":"address"},{"internalType":"uint256","name":"vaultBufferMantissa_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"contract InterestRateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isPToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"contract PTokenInterface","name":"pTokenCollateral","type":"address"}],"name":"liquidateBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"magmaVault","outputs":[{"internalType":"contract IMagma","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxFlashLoanRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"mintAmount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdmin","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingRedemptionId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"peridottroller","outputs":[{"internalType":"contract PeridottrollerInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolSeizeShareMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemTokens","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"redeemAmount","type":"uint256"}],"name":"redeemUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"referralId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repayBorrowBehalf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactorMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyRatePerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract EIP20NonStandardInterface","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":[],"name":"vaultBufferMantissa","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

60808060405234601557614d11908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146104e457806308cf1d50146104df578063095ea7b3146104da5780630e752702146104d55780630ed14e63146104d0578063153ab505146104cb578063166aa708146104c6578063173b9904146104c157806317bfdfbc146104bc57806318160ddd146104b7578063182df0f5146104b25780631a31d465146104ad5780631be19560146104a857806323b872dd146104a35780632608f8181461049e578063267822471461049957806327d699f914610494578063313ce5671461048f578063358e0b001461048a5780633af9e669146104855780633b1d21a2146104805780633e9410101461047b57806343e9aa641461047657806347bd37181461047157806356e677281461046c5780635a890c0e146104675780635c60da1b146104625780635cffe9de1461045d578063601a0bf114610458578063613255ab146104535780636752e7021461044e57806369ab3250146104495780636c540baf146104445780636f307dc31461043f57806370a082311461043a57806373acee981461043557806373d025d614610430578063852a12e31461042b5780638827dbe4146104265780638f840ddd1461042157806395d89b411461041c57806395dd91931461041757806399d8c1b4146104125780639b503bae1461040d578063a0712d6814610408578063a07eaa0614610403578063a252d8be146103fe578063a456365d146103f9578063a6afed95146103f4578063a9059cbb146103ef578063aa5af0fd146103ea578063ae4d65c7146103e5578063ae9d70b0146103e0578063b2a02ff1146103db578063b71d1a0c146103d6578063bd6d894d146103d1578063be7de3f8146103cc578063c0c7347e146103c7578063c37f68e2146103c2578063c3b68700146103bd578063c423fe29146103b8578063c53d59d9146103b3578063c5ebeaec146103ae578063d5c6d3bd146103a9578063d9d98ce4146103a4578063db006a751461039f578063dcef7fbd1461039a578063dd62ed3e14610395578063e9c714f214610390578063ef94c6451461038b578063f2b3abbd14610386578063f3fdb15a14610381578063f5e3c4621461037c578063f6f1024914610377578063f851a44014610372578063f8f9da281461036d5763fca7820b14610368575f80fd5b612a53565b6129f1565b6129c5565b61299d565b612599565b612571565b612541565b612466565b612345565b6122e6565b6122bf565b612215565b6121bb565b612194565b612018565b611f65565b611f3d565b611e6a565b611e08565b611deb565b611dce565b611db4565b611d0c565b611ccb565b611c26565b611bca565b611bad565b611b56565b611b3c565b611b1f565b611a9a565b611a02565b61185d565b61183b565b6117b9565b611792565b6116ed565b6116d0565b6116ab565b61146a565b611448565b611403565b6113c8565b61139c565b61137f565b611365565b611344565b61131d565b6111f4565b611167565b61113f565b611124565b611067565b61104a565b610f50565b610e8a565b610e70565b610e07565b610d22565b610d02565b610cde565b610cb6565b610c57565b610c15565b610abb565b610a2f565b61097c565b61095f565b610908565b6108eb565b610832565b6107b0565b610793565b610742565b6106b6565b610688565b6105ae565b5f9103126104f357565b5f80fd5b90600182811c92168015610525575b602083101461051157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610506565b634e487b7160e01b5f52604160045260245ffd5b602081019081106001600160401b0382111761055e57604052565b61052f565b90601f801991011681019081106001600160401b0382111761055e57604052565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b346104f3575f3660031901126104f3576040515f6001546105ce816104f7565b80845290600181169081156106645750600114610606575b610602836105f681850382610563565b60405191829182610584565b0390f35b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b80821061064a575090915081016020016105f66105e6565b919260018160209254838588010152019101909291610632565b60ff191660208086019190915291151560051b840190910191506105f690506105e6565b346104f3575f3660031901126104f3576020601654604051908152f35b6001600160a01b038116036104f357565b346104f35760403660031901126104f3576004356106d3816106a5565b60243590335f52600f602052816106fd8260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a360405160018152602090f35b346104f35760203660031901126104f35761077d6004355f5461076760ff8216612b5e565b60ff19165f5561077561383f565b503333614439565b50600160ff195f5416175f5560206040515f8152f35b346104f3575f3660031901126104f3576020601254604051908152f35b346104f3575f3660031901126104f35760035460081c6001600160a01b031633036107d757005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346104f35760203660031901126104f35760043561085e60018060a01b0360035460081c163314612b25565b670de0b6b3a764000081116108b5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef9060165461089b82601655565b604080519182526020820192909252a16108b3613d26565b005b60405162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b210313ab33332b960911b6044820152606490fd5b346104f3575f3660031901126104f3576020600854604051908152f35b346104f35760203660031901126104f357602061094c60043561092a816106a5565b5f5461093860ff8216612b5e565b60ff19165f5561094661383f565b506140ef565b600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f3576020600d54604051908152f35b346104f3575f3660031901126104f3576020610996613d83565b604051908152f35b604435906109ab826106a5565b565b604051906109ab602083610563565b9291926001600160401b03821161055e57604051916109e5601f8201601f191660200184610563565b8294818452818301116104f3578281602093845f960137010152565b9080601f830112156104f357816020610a1c933591016109bc565b90565b60c4359060ff821682036104f357565b346104f35760e03660031901126104f357600435610a4c816106a5565b60243590610a59826106a5565b60443591610a66836106a5565b6064356084356001600160401b0381116104f357610a88903690600401610a01565b9060a435946001600160401b0386116104f357610aac6108b3963690600401610a01565b93610ab5610a1f565b95612bb1565b346104f35760203660031901126104f357600435610ad8816106a5565b60035460081c6001600160a01b031690610afc6001600160a01b0383163314612c36565b6013546001600160a01b039182169291610b3091610b289160089190911c165b6001600160a01b031690565b831415612c9a565b6040516370a0823160e01b8152306004820152602081602481865afa908115610bb5575f91610bba575b50823b156104f35760405163a9059cbb60e01b81526001600160a01b0390921660048301526024820152905f908290818381604481015b03925af18015610bb557610ba157005b80610baf5f6108b393610563565b806104e9565b612ba6565b610bdc915060203d602011610be2575b610bd48183610563565b810190612b97565b5f610b5a565b503d610bca565b60609060031901126104f357600435610c01816106a5565b90602435610c0e816106a5565b9060443590565b346104f3576020610c43610c2836610be9565b915f54610c3760ff8216612b5e565b60ff19165f5533613ddf565b15600160ff195f5416175f55604051908152f35b346104f35760403660031901126104f357610c9e600435610c77816106a5565b602435905f54610c8960ff8216612b5e565b60ff19165f55610c9761383f565b5033614439565b505f805460ff19166001178155604051908152602090f35b346104f3575f3660031901126104f3576004546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f3576020610cf8612d01565b6040519015158152f35b346104f3575f3660031901126104f357602060ff60035416604051908152f35b346104f35760403660031901126104f357600435610d3f816106a5565b60243590610d5b60018060a01b0360035460081c163314612b25565b6001600160a01b03168015610dd5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef91610db0610dd09260018060a01b03166001600160601b0360a01b6015541617601555565b610db981601655565b604051918291829190602060408401935f81520152565b0390a1005b60405162461bcd60e51b815260206004820152600a6024820152691e995c9bc81d985d5b1d60b21b6044820152606490fd5b346104f35760203660031901126104f3576020670de0b6b3a7640000610e66600435610e32816106a5565b610e3a6139b5565b9060405191610e4883610543565b82526001600160a01b03165f908152600e85526040902054906141fa565b5104604051908152f35b346104f3575f3660031901126104f3576020610996613fbc565b346104f35760203660031901126104f3576004355f54610eac60ff8216612b5e565b60ff19165f55610eba61383f565b506009544303610f3d57610ece90336149da565b600c54818101809111610f3857600c8190556040805133815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59080606081015b0390a1610f2d600160ff195f5416175f55565b6040515f8152602090f35b61304e565b6338acf79960e01b5f525f60045260245ffd5b346104f35760203660031901126104f357600435600354610f8790610f809060081c6001600160a01b0316610b1c565b3314612b25565b610f9360185415612d12565b601554610fa8906001600160a01b0316610b1c565b604051633ea0e43760e11b815260048101839052306024820181905260448201529190602090839060649082905f905af18015610bb5577fd5ba0459265c0cfa94aa9d715cc0b5c1305e25f72d371f73e627359065e83fdc925f9161102b575b5061101281601855565b6040805191825260208201929092529081908101610dd0565b611044915060203d602011610be257610bd48183610563565b5f611008565b346104f3575f3660031901126104f3576020600b54604051908152f35b346104f35760203660031901126104f3576004356001600160401b0381116104f357366023820112156104f3576110a89036906024816004013591016109bc565b6110c060018060a01b0360035460081c163314612b25565b8051806110c957005b81604091810103126104f35760406020820151916110e6836106a5565b01516001600160a01b0390911690816110fb57005b61111f6108b39260018060a01b03166001600160601b0360a01b6015541617601555565b601655565b346104f3575f3660031901126104f357602060405160018152f35b346104f3575f3660031901126104f3576014546040516001600160a01b039091168152602090f35b346104f35760803660031901126104f357600435611184816106a5565b60243590611191826106a5565b604435606435916001600160401b0383116104f357366023840112156104f3578260040135916001600160401b0383116104f35736602484860101116104f3576106029460246111e2950192612d53565b60405190151581529081906020820190565b346104f35760203660031901126104f3576004355f5461121660ff8216612b5e565b60ff19165f5561122461383f565b5060035461123d9060081c6001600160a01b0316610b1c565b330361130e5760095443036112ff5780611255613fbc565b106112f057600c54908181116112e157611290817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e93613186565b61129981600c55565b6003546112b390839060081c6001600160a01b0316613fd8565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101610f1a565b6378d2980560e11b5f5260045ffd5b633345e99960e01b5f5260045ffd5b630dff50cb60e41b5f5260045ffd5b630f7e5e6d60e41b5f5260045ffd5b346104f35760203660031901126104f357602061099660043561133f816106a5565b6131a0565b346104f3575f3660031901126104f3576020604051666379da05b600008152f35b346104f3575f3660031901126104f35760206040515f8152f35b346104f3575f3660031901126104f3576020600954604051908152f35b346104f3575f3660031901126104f35760135460405160089190911c6001600160a01b03168152602090f35b346104f35760203660031901126104f3576004356113e5816106a5565b60018060a01b03165f52600e602052602060405f2054604051908152f35b346104f3575f3660031901126104f3575f5461142160ff8216612b5e565b60ff19165f5561142f61383f565b506020600b54600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f357602060ff601754166040519015158152f35b346104f35760203660031901126104f3576004355f5461148c60ff8216612b5e565b60ff19165f5561149a61383f565b506114a5600161482d565b6114c16114b0613d83565b6114b86109ad565b90815282614beb565b6005549091906114d9906001600160a01b0316610b1c565b6020604051809263eabe7d9160e01b8252815f816114fc89333060048501614896565b03925af1908115610bb5575f91611682575b5080611670575060095443036116615780611527613fbc565b106116525761154061153b83600d54613186565b600d55565b335f908152600e6020526040902061155b9083905b54613186565b335f908152600e60205260409020556115748133613fd8565b604051828152309033905f516020614cbc5f395f51905f5290602090a360408051338152602081018390529081018390527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a16005546115e0906001600160a01b0316610b1c565b91823b156104f3576040516351dff98960e01b815230600482015233602482015260448101929092526064820152905f908290608490829084905af18015610bb55761163e575b5f805460ff19166001178155604051908152602090f35b80610baf5f61164c93610563565b5f611627565b6391240a1b60e01b5f5260045ffd5b6397b5cfcd60e01b5f5260045ffd5b635bd5f7ab60e11b5f5260045260245ffd5b61169b915060203d602011610be257610bd48183610563565b5f61150e565b801515036104f357565b346104f35760203660031901126104f3576108b36004356116cb816116a1565b6131e1565b346104f3575f3660031901126104f3576020600c54604051908152f35b346104f3575f3660031901126104f3576040515f60025461170d816104f7565b8084529060018116908115610664575060011461173457610602836105f681850382610563565b60025f9081527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace939250905b808210611778575090915081016020016105f66105e6565b919260018160209254838588010152019101909291611760565b346104f35760203660031901126104f35760206109966004356117b4816106a5565b6140ef565b346104f35760c03660031901126104f3576004356117d6816106a5565b6024356117e2816106a5565b6044356064356001600160401b0381116104f357611804903690600401610a01565b6084356001600160401b0381116104f357611823903690600401610a01565b9160a4359360ff851685036104f3576108b3956135b4565b346104f3575f3660031901126104f357602060ff601354166040519015158152f35b346104f35760203660031901126104f3576004355f5461187f60ff8216612b5e565b60ff19165f5561188d61383f565b506005546118a3906001600160a01b0316610b1c565b60206040518092634ef4c3e160e01b8252815f816118c688333060048501614896565b03925af1908115610bb5575f916119d6575b50806119c4575060095443036119b5577f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f611930611929611917613d83565b936119206109ad565b948552336149da565b9283614beb565b809261194161153b83600d54613193565b335f908152600e6020526040902061195c9083905b54613193565b335f908152600e602052604090205560408051338152602081019290925281019190915280606081010390a1604051908152339030905f516020614cbc5f395f51905f5290602090a3610f2d600160ff195f5416175f55565b6338d8859760e01b5f5260045ffd5b6303171a8760e61b5f5260045260245ffd5b6119ef915060203d602011610be257610bd48183610563565b5f6118d8565b60e435906109ab826106a5565b346104f3576101203660031901126104f357600435611a20816106a5565b60243590611a2d826106a5565b611a3561099e565b91606435926084356001600160401b0381116104f357611a59903690600401610a01565b9360a435946001600160401b0386116104f357611a7d6108b3963690600401610a01565b91611a86610a1f565b93611a8f6119f5565b9561010435976136ec565b346104f35760203660031901126104f357600435611ab7816106a5565b600354611ad19060081c6001600160a01b0316331461378e565b601354611aeb90610b1c9060081c6001600160a01b031681565b803b156104f3576040516317066a5760e21b81526001600160a01b0383166004820152905f90829081838160248101610b91565b346104f3575f3660031901126104f3576020601954604051908152f35b346104f3575f3660031901126104f357602061099661383f565b346104f35760403660031901126104f357611b95600435611b76816106a5565b602435905f54611b8860ff8216612b5e565b60ff19165f553380613ddf565b5f805460ff1916600117905560405190158152602090f35b346104f3575f3660031901126104f3576020600a54604051908152f35b346104f35760203660031901126104f3577f66f1d115ba8b08c31c22d86374bc36cea7c2771ef78949fa59328010115b4b9c6020600435611c1960018060a01b0360035460081c163314612b25565b80601955604051908152a1005b346104f3575f3660031901126104f3576006546001600160a01b03166020611c4c613fbc565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa8015610bb557610602915f91611cac575b506040519081529081906020820190565b611cc5915060203d602011610be257610bd48183610563565b5f611c9b565b346104f357611cf7611cdc36610be9565b915f54611ceb60ff8216612b5e565b60ff19165f5533614225565b600160ff195f5416175f5560206040515f8152f35b346104f35760203660031901126104f357600435611d29816106a5565b60035460081c6001600160a01b03163303611da557600480546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99190819081015b0390a16040515f8152602090f35b635cb56c2b60e01b5f5260045ffd5b346104f3575f3660031901126104f35760206109966139b5565b346104f3575f3660031901126104f3576020601854604051908152f35b346104f3575f3660031901126104f3576020601154604051908152f35b346104f35760203660031901126104f3576080600435611e27816106a5565b6001600160a01b0381165f908152600e602052604090205490611e49906140ef565b611e51613d83565b90604051925f8452602084015260408301526060820152f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611ee85780611ec36127107f84e3f33c20d693ca1e98ee0ab87bf817997a96baa0819ed94c5b96f4a6663f139311156139e8565b601254611ecf82601255565b6040805191825260208201929092529081908101611d97565b60405162461bcd60e51b815260206004820152602760248201527f4f6e6c792061646d696e2063616e20736574206d617820666c617368206c6f616044820152666e20726174696f60c81b6064820152608490fd5b346104f3575f3660031901126104f3576005546040516001600160a01b039091168152602090f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611fc95780611fbd60647f2cd87d6d243502fbf87bd7dd851659e05ac6a614994d7f04592e4f173f939783931115613a34565b601154611ecf82601155565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c792061646d696e2063616e2073657420666c617368206c6f616e2066656044820152606560f81b6064820152608490fd5b346104f35760203660031901126104f3576004355f5461203a60ff8216612b5e565b60ff19165f5561204861383f565b5060055461205e906001600160a01b0316610b1c565b6020604051809263368f515360e21b8252815f8161208188333060048501614896565b03925af1908115610bb5575f91612175575b50806121635750600954430361215457806120ac613fbc565b1061214557806120e57f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80926120e0336140ef565b613193565b90610f1a6120f582600b54613193565b335f908152601060205260409020848155600a54600190910155600b81905561211e8333613fd8565b60408051338152602081019490945283019390935260608201929092529081906080820190565b6348c2588160e01b5f5260045ffd5b630e8d8c6160e21b5f5260045ffd5b635684deff60e11b5f5260045260245ffd5b61218e915060203d602011610be257610bd48183610563565b5f612093565b346104f35760203660031901126104f35760206109966004356121b6816106a5565b613ae1565b346104f35760403660031901126104f3576127106122086004356121de816106a5565b601354602435916121ff9160081c6001600160a01b03908116911614612f56565b60115490613062565b6040519190048152602090f35b346104f35760203660031901126104f3576004355f5461223760ff8216612b5e565b60ff19165f5561224561383f565b50801590811591826122b7575b61225b9061482d565b612263613d83565b9161226c6109ad565b928352156122a6576122828161229192936141fa565b670de0b6b3a764000090510490565b6005546114d9906001600160a01b0316610b1c565b506122b1905f614beb565b5f612291565b506001612252565b346104f35760203660031901126104f3576020610cf86004356122e1816116a1565b613bf8565b346104f35760403660031901126104f357602061233c600435612308816106a5565b60243590612315826106a5565b60018060a01b03165f52600f835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346104f3575f3660031901126104f3576004546001600160a01b03168033811480159061245e575b61244f576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc916123f6906123d19060081c6001600160a01b0316610b1c565b91600354610100600160a81b03191660089190911b610100600160a81b031617600355565b600480546001600160a01b0319169055600354604080516001600160a01b03938416815260089290921c9092166020820152a1600454604080516001600160a01b03938416815292909116602083015281908101611d97565b631ba24f2960e21b5f5260045ffd5b50331561236d565b346104f3575f3660031901126104f35760035461249290610f809060081c6001600160a01b0316610b1c565b6018546124a0811515613ce2565b6015546124b5906001600160a01b0316610b1c565b604051635d043b2960e11b815260048101839052306024820181905260448201529190602090839060649082905f905af1908115610bb5577fc220b62a9bc2190b38c6c59a97aeca3788b99c20f6ba14f5f02b144a1d79dd43925f92612520575b5061089b5f601855565b61253a91925060203d602011610be257610bd48183610563565b905f612516565b346104f35760203660031901126104f3576020610996600435612563816106a5565b61256b61383f565b5061412f565b346104f3575f3660031901126104f3576006546040516001600160a01b039091168152602090f35b346104f35760603660031901126104f3576004356125b6816106a5565b602435906044356125c6816106a5565b5f546125d460ff8216612b5e565b60ff19165f556125e261383f565b5060405163a6afed9560e01b81526001600160a01b039190911692906020816004815f885af1908115610bb5575f9161297e575b508061296c5750600554612632906001600160a01b0316610b1c565b604051632fe3f38f60e11b81523060048201526001600160a01b038581166024830152336044830152841660648201526084810183905290602090829060a49082905f905af1908115610bb5575f9161294d575b508061293b5750600954430361292c57604051636c540baf60e01b8152602081600481875afa908115610bb5575f9161290d575b5043036128fe576001600160a01b03821633146128ef5780156128e0575f1981146128d1576126ea908233614439565b600554909190612702906001600160a01b0316610b1c565b6040805163c488847b60e01b815294918590818061272588873060048501614896565b03915afa938415610bb5575f905f9561289d575b5061274490156148e3565b6040516370a0823160e01b81526001600160a01b0383166004820152602081602481855afa908115610bb5576127859186915f9161287e575b50101561494b565b3081036127f55783610f1a916127be7f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5296853330614225565b604080513381526001600160a01b0395861660208201529081019590955290921660608401526080830191909152819060a0820190565b60405163b2a02ff160e01b8152936020858061281684873360048501614896565b03815f865af1918215610bb55761285a610f1a937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52975f9161285f575b5015614997565b6127be565b612878915060203d602011610be257610bd48183610563565b5f612853565b612897915060203d602011610be257610bd48183610563565b5f61277d565b61274495506128c4915060403d6040116128ca575b6128bc8183610563565b8101906148cd565b94612739565b503d6128b2565b635982c5bb60e11b5f5260045ffd5b63d29da7ef60e01b5f5260045ffd5b631bd1a62160e21b5f5260045ffd5b631046f38d60e31b5f5260045ffd5b612926915060203d602011610be257610bd48183610563565b5f6126ba565b6380965b1b60e01b5f5260045ffd5b632d33e4f360e21b5f5260045260245ffd5b612966915060203d602011610be257610bd48183610563565b5f612686565b633eea49b760e11b5f5260045260245ffd5b612997915060203d602011610be257610bd48183610563565b5f612616565b346104f3575f3660031901126104f3576015546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f35760035460405160089190911c6001600160a01b03168152602090f35b346104f3575f3660031901126104f357600654611c86906020906001600160a01b0316612a1c613fbc565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346104f35760203660031901126104f3576004355f54612a7560ff8216612b5e565b60ff19165f55612a8361383f565b5060035460081c6001600160a01b03163303612b16576009544303612b0757670de0b6b3a76400008111612af8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854612adf82600855565b6040805191825260208201929092529081908101610f1a565b63717220f360e11b5f5260045ffd5b637dfca6b760e11b5f5260045ffd5b631205b57b60e11b5f5260045ffd5b15612b2c57565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b15612b6557565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b908160209103126104f3575190565b6040513d5f823e3d90fd5b9294612be994602096612bc794600499946135b4565b601354610100600160a81b03191660089190911b610100600160a81b03161790565b60138190556040516318160ddd60e01b8152928391829060081c6001600160a01b03165afa8015610bb557612c1b5750565b612c339060203d602011610be257610bd48183610563565b50565b15612c3d57565b60405162461bcd60e51b815260206004820152602f60248201527f5045726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b15612ca157565b60405162461bcd60e51b815260206004820152603260248201527f5045726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b60185415612d0e57600190565b5f90565b15612d1957565b60405162461bcd60e51b8152602060048201526012602482015271726564656d7074696f6e2070656e64696e6760701b6044820152606490fd5b915f54612d6260ff8216612b5e565b60ff19165f55601354612d809060081c6001600160a01b0316610b1c565b6001600160a01b0383169590612d97908714612f56565b612db2612dad612da960135460ff1690565b1590565b612fa2565b60405163613255ab60e01b81526001600160a01b0384166004820152602081602481305afa8015610bb557612e4d94612df985926020945f91612f39575b50831115612ff8565b612e10612e0860115484613062565b612710900490565b958694612e1b613fbc565b6001600160a01b0390991699612e31858c613fd8565b6040516323e30c8b60e01b815296879586953360048801613093565b03815f895af1908115610bb5577f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f094612eb67f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9612ed894612ecf945f91612f1a575b50146130e3565b612ebe613fbc565b612eca8282101561313a565b613186565b9283101561313a565b81612f01575b604080519182526020820192909252a36001906109ab600160ff195f5416175f55565b612f15612f1083600c54613193565b600c55565b612ede565b612f33915060203d602011610be257610bd48183610563565b5f612eaf565b612f509150853d8711610be257610bd48183610563565b5f612df0565b15612f5d57565b60405162461bcd60e51b815260206004820152601e60248201527f466c6173684c6f616e3a20746f6b656e206e6f7420737570706f7274656400006044820152606490fd5b15612fa957565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20666c617368206c6f616e73206172652070617573656044820152601960fa1b6064820152608490fd5b15612fff57565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20616d6f756e742065786365656473206d6178696d756044820152606d60f81b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610f3857565b811561307f570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160a01b039182168152911660208201526040810191909152606081019190915260a060808201819052810183905260c09290918190848401375f828201840152601f01601f1916010190565b156130ea57565b60405162461bcd60e51b815260206004820152602260248201527f466c6173684c6f616e3a20696e76616c69642063616c6c6261636b2072657475604482015261393760f11b6064820152608490fd5b1561314157565b60405162461bcd60e51b815260206004820152601a60248201527f466c6173684c6f616e3a206c6f616e206e6f74207265706169640000000000006044820152606490fd5b91908203918211610f3857565b91908201809211610f3857565b601354906001600160a01b03600883901c81169116036131dc5760ff16612d0e576127106131d86131cf613fbc565b60125490613062565b0490565b505f90565b6131f960018060a01b0360035460081c163314612b25565b60175490151560ff8216151581146132475760ff1990911660ff8216176017556040519081527fe99ce3808ef0a5bbecc8824375b67638e945a147d2836eaf2a2c2b8f482bb54e90602090a1565b5050565b1561325257565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b156132aa57565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561330f57565b60405162461bcd60e51b815260206004820152601d60248201527f73657474696e672070657269646f7474726f6c6c6572206661696c65640000006044820152606490fd5b1561335b57565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b601f82116133b857505050565b5f5260205f20906020601f840160051c830193106133f0575b601f0160051c01905b8181106133e5575050565b5f81556001016133da565b90915081906133d1565b9081516001600160401b03811161055e576134218161341a6001546104f7565b60016133ab565b602092601f821160011461346157613451929382915f92613456575b50508160011b915f199060031b1c19161790565b600155565b015190505f8061343d565b60015f52601f198216937fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f5b8681106134c557508360019596106134ad575b505050811b01600155565b01515f1960f88460031b161c191690555f80806134a2565b9192602060018192868501518155019401920161348f565b9081516001600160401b03811161055e57613504816134fd6002546104f7565b60026133ab565b602092601f821160011461353857613533929382915f926134565750508160011b915f199060031b1c19161790565b600255565b60025f52601f198216937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b86811061359c5750836001959610613584575b505050811b01600255565b01515f1960f88460031b161c191690555f8080613579565b91926020600181928685015181550194019201613566565b60035490959492939060081c6001600160a01b03166001600160a01b0316330361369b5761364361363d6136489361361e61361861365d9a61360b61364d9a6009541580613691575b6136069061324b565b600755565b6121b660075415156132a3565b15613308565b61362743600955565b613638670de0b6b3a7640000600a55565b61412f565b15613354565b6133fa565b6134dd565b60ff1660ff196003541617600355565b6136676005601155565b613672610bb8601255565b61368160ff1960135416601355565b6109ab600160ff195f5416175f55565b50600a54156135fd565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b90919293949596976009541580613784575b15613750576003546109ab9961374891610100600160a81b0319163360081b610100600160a81b03161760035560018060a01b03166001600160601b0360a01b6015541617601555565b601655612bb1565b60405162461bcd60e51b815260206004820152600c60248201526b185b1c9958591e481a5b9a5d60a21b6044820152606490fd5b50600a54156136fe565b1561379557565b60405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207468652061646d696e206d6179207365742074686520706572696460448201526f6f742d6c696b652064656c656761746560801b6064820152608490fd5b156137fa57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b6009544381146131dc57613851613fbc565b600b5490600c5492600a5492613871610b1c60065460018060a01b031690565b6040516315f2405360e01b81526004810185905260248101839052604481018790529590602090879060649082905afa958615610bb55761394a866120e06122826139389961394461391b7f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049d6139899b613966995f92613990575b5061390a9061390465048c273950008411156137f3565b43613186565b906139136109ad565b9081526141fa565b976120e061228261392f6122828d8d6141fa565b9e8f9c8d613193565b9b6008546139136109ad565b966141fa565b9161395443600955565b61395d83600a55565b612f1084600b55565b604051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b61390a9192506139ae9060203d602011610be257610bd48183610563565b91906138ed565b5f546139c360ff8216612b5e565b60ff19165f556139d161383f565b506139da613d83565b90600160ff195f5416175f55565b156139ef57565b60405162461bcd60e51b815260206004820152601960248201527f466c617368206c6f616e20726174696f20746f6f2068696768000000000000006044820152606490fd5b15613a3b57565b60405162461bcd60e51b815260206004820152601760248201527f466c617368206c6f616e2066656520746f6f20686967680000000000000000006044820152606490fd5b908160209103126104f35751610a1c816116a1565b15613a9c57565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b600354613af99060081c6001600160a01b0316610b1c565b3303613bd4576005546001600160a01b031660405163fa707af960e01b815291906020836004816001600160a01b0386165afa928315610bb5577ff911792cf103798503b6dab0992e023d45661865683c550a919b8c9ad07dbf7f93613b66915f91613ba5575b50613a95565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b03928316815292909116602083015281908101613989565b613bc7915060203d602011613bcd575b613bbf8183610563565b810190613a80565b5f613b60565b503d613bb5565b634c2fa00360e01b5f5260045ffd5b908160209103126104f35751610a1c816106a5565b600354613c109060081c6001600160a01b0316610b1c565b33148015613c61575b156131dc57613c59816111e27f7958d1d41a26870ec9486cb4dc33830b446f038d00661e064b50719bdde7f42c9360ff8019601354169115151617601355565b0390a1600190565b5060046020613c80610b1c610b1c610b1c60055460018060a01b031690565b604051631251eb1160e11b815292839182905afa908115610bb5575f91613cb3575b506001600160a01b03163314613c19565b613cd5915060203d602011613cdb575b613ccd8183610563565b810190613be3565b5f613ca2565b503d613cc3565b15613ce957565b60405162461bcd60e51b81526020600482015260156024820152743737903832b73234b733903932b232b6b83a34b7b760591b6044820152606490fd5b60ff601754166109ab57613d386145da565b613d40614633565b8101808211610f3857801561324757613d65670de0b6b3a76400009160165490613062565b04808211613d71575050565b8103908111610f38576109ab906146cd565b600d5480613d92575060075490565b613d9a613fbc565b90600b548201809211610f3857600c549182810392818411610f3857670de0b6b3a76400008402938404670de0b6b3a76400001491141715610f3857610a1c91613075565b600554613df4906001600160a01b0316610b1c565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152851660448201526064810186905290602090829060849082905f905af1908115610bb5575f91613f9d575b5080613f8b57506001600160a01b038381169490831693848614613f7c575f516020614cbc5f395f51905f5293613f12936001600160a01b0381168703613f54575f195b613e908582613186565b93613ef8613eb2876115558760018060a01b03165f52600e60205260405f2090565b91613ed1886119568360018060a01b03165f52600e60205260405f2090565b6001600160a01b038781165f908152600e6020526040808220969096559216825292902090565b5519613f19575b5050604051918252509081906020820190565b0390a35f90565b613f36613f4b9260018060a01b03165f52600f60205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080613eff565b6001600160a01b0382165f908152600f60205260409020613f76908290613f36565b54613e86565b638cd22d1960e01b5f5260045ffd5b630c93cb5b60e01b5f5260045260245ffd5b613fb6915060203d602011610be257610bd48183610563565b5f613e42565b613fc46145da565b613fcc614633565b8101809111610f385790565b81613fe16145da565b1061408d5760135461400090610b1c9060081c6001600160a01b031681565b91823b156104f35760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015610bb557614079575b503d801561406e57602014614057575f80fd5b60205f803e6140665f516147e1565b6109ab613d26565b506140665f196147e1565b80610baf5f61408793610563565b5f614044565b60405162461bcd60e51b815260206004820152603460248201527f696e73756666696369656e7420627566666572202d2061646d696e206d7573746044820152731031b7b6b83632ba32903932b232b6b83a34b7b760611b6064820152608490fd5b6001600160a01b03165f9081526010602052604090208054908115614129576001614120610a1c93600a5490613062565b91015490613075565b50505f90565b6003546141479060081c6001600160a01b0316610b1c565b33036141eb5760095443036141dc576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa928315610bb5577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936141bc915f91613ba55750613a95565b600680546001600160a01b0319166001600160a01b038416179055613b82565b630be2a5cb60e11b5f5260045ffd5b63407fded560e01b5f5260045ffd5b90614213915f60405161420c81610543565b5251613062565b6040519061422082610543565b815290565b92602083829395865f614242610b1c60055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af1908115610bb5575f9161441a575b508061440857506001600160a01b03838116939083168085146143f9577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc594815f516020614cbc5f395f51905f526143bb614342976143056142f56109ad565b666379da05b600008152896148b8565b96614310888a613186565b9161438861436d61432e6122828c614326613d83565b6139136109ad565b9b61433b8d600c54613193565b9d8e600c55565b61435161153b8d600d54613186565b6001600160a01b0384165f908152600e60205260409020611555565b6001600160a01b039092165f908152600e6020526040902090565b556143aa61436d836119568460018060a01b03165f52600e60205260405f2090565b556040519081529081906020820190565b0390a360405191825230915f516020614cbc5f395f51905f5290602090a360408051308152602081019290925281019190915280606081015b0390a1565b633a94626760e11b5f5260045ffd5b6305a647b960e31b5f5260045260245ffd5b614433915060203d602011610be257610bd48183610563565b5f614295565b600554909190614451906001600160a01b0316610b1c565b604051631200453160e11b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082905f905af1908115610bb5575f916145bb575b50806145a95750600954430361459a577f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1916144da826140ef565b935f19810361458b57506145856144fc6144f5865b846149da565b8096613186565b61450886600b54613186565b90806145248660018060a01b03165f52601060205260405f2090565b55600a546001600160a01b0386165f9081526010602052604090206001015561454c82600b55565b604080516001600160a01b03958616815295909416602086015292840186905260608401929092526080830191909152819060a0820190565b0390a190565b6144fc6144f5614585926144ef565b63c9021e2f60e01b5f5260045ffd5b6335a6a70b60e21b5f5260045260245ffd5b6145d4915060203d602011610be257610bd48183610563565b5f61449f565b6013546040516370a0823160e01b8152306004820152906020908290602490829060081c6001600160a01b03165afa908115610bb5575f9161461a575090565b610a1c915060203d602011610be257610bd48183610563565b601554614648906001600160a01b0316610b1c565b6040516370a0823160e01b815230600482015290602082602481845afa918215610bb5575f926146ac575b508115614129576040516303d1689d60e11b81526004810192909252602090829060249082905afa908115610bb5575f9161461a575090565b6146c691925060203d602011610be257610bd48183610563565b905f614673565b801580156147d5575b612c33576013545f91906147169082906146fb9060081c6001600160a01b0316610b1c565b601554614710906001600160a01b0316610b1c565b90614b16565b60155460209061476690614732906001600160a01b0316610b1c565b6019546040516364ee50b160e01b815260048101869052306024820152604481019190915294859283919082906064820190565b03925af1908115610bb5577f6c89b3dfee140ff66a50284d3adbd08d9d66970bf92bfbbb60e8ed1dda9dec04925f926147b4575b5060408051918252602082019290925290819081016143f4565b6147ce91925060203d602011610be257610bd48183610563565b905f61479a565b5060175460ff166146d6565b156147e857565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b1561483457565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6001600160a01b03918216815291166020820152604081019190915260600190565b6131d890670de0b6b3a7640000925190613062565b91908260409103126104f3576020825192015190565b156148ea57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b1561495257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561499e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6013546040516370a0823160e01b815230600482015260089190911c6001600160a01b0316929091602083602481875afa928315610bb5575f93614af5575b50833b156104f3576040516323b872dd60e01b8152915f9183918291614a459190309060048501614896565b038183875af18015610bb557614ae1575b503d8015614ad657602014614a69575f80fd5b60205f803e614a785f51614c17565b6040516370a0823160e01b815230600482015291602090839060249082905afa8015610bb557614aaf925f91614ab7575b50613186565b610a1c613d26565b614ad0915060203d602011610be257610bd48183610563565b5f614aa9565b50614a785f19614c17565b80610baf5f614aef93610563565b5f614a56565b614b0f91935060203d602011610be257610bd48183610563565b915f614a19565b60405163095ea7b360e01b60208281019182526001600160a01b0385166024840152604480840196909652948252929390925f90614b55606486610563565b84519082855af15f51903d81614bbf575b501590505b614b7457505050565b60405163095ea7b360e01b60208201526001600160a01b039390931660248401525f60448085019190915283526109ab92614bba90614bb4606482610563565b82614c63565b614c63565b15159050614bdf5750614b6b6001600160a01b0382163b15155b5f614b66565b6001614b6b9114614bd9565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610f3857610a1c915190613075565b15614c1e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b905f602091828151910182855af115612ba6575f513d614cb257506001600160a01b0381163b155b614c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614c8b56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122041851f2064eb0319c3b4be00ac29637f6834fd1a22c297eeb8f839c535abd4db64736f6c634300081e0033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146104e457806308cf1d50146104df578063095ea7b3146104da5780630e752702146104d55780630ed14e63146104d0578063153ab505146104cb578063166aa708146104c6578063173b9904146104c157806317bfdfbc146104bc57806318160ddd146104b7578063182df0f5146104b25780631a31d465146104ad5780631be19560146104a857806323b872dd146104a35780632608f8181461049e578063267822471461049957806327d699f914610494578063313ce5671461048f578063358e0b001461048a5780633af9e669146104855780633b1d21a2146104805780633e9410101461047b57806343e9aa641461047657806347bd37181461047157806356e677281461046c5780635a890c0e146104675780635c60da1b146104625780635cffe9de1461045d578063601a0bf114610458578063613255ab146104535780636752e7021461044e57806369ab3250146104495780636c540baf146104445780636f307dc31461043f57806370a082311461043a57806373acee981461043557806373d025d614610430578063852a12e31461042b5780638827dbe4146104265780638f840ddd1461042157806395d89b411461041c57806395dd91931461041757806399d8c1b4146104125780639b503bae1461040d578063a0712d6814610408578063a07eaa0614610403578063a252d8be146103fe578063a456365d146103f9578063a6afed95146103f4578063a9059cbb146103ef578063aa5af0fd146103ea578063ae4d65c7146103e5578063ae9d70b0146103e0578063b2a02ff1146103db578063b71d1a0c146103d6578063bd6d894d146103d1578063be7de3f8146103cc578063c0c7347e146103c7578063c37f68e2146103c2578063c3b68700146103bd578063c423fe29146103b8578063c53d59d9146103b3578063c5ebeaec146103ae578063d5c6d3bd146103a9578063d9d98ce4146103a4578063db006a751461039f578063dcef7fbd1461039a578063dd62ed3e14610395578063e9c714f214610390578063ef94c6451461038b578063f2b3abbd14610386578063f3fdb15a14610381578063f5e3c4621461037c578063f6f1024914610377578063f851a44014610372578063f8f9da281461036d5763fca7820b14610368575f80fd5b612a53565b6129f1565b6129c5565b61299d565b612599565b612571565b612541565b612466565b612345565b6122e6565b6122bf565b612215565b6121bb565b612194565b612018565b611f65565b611f3d565b611e6a565b611e08565b611deb565b611dce565b611db4565b611d0c565b611ccb565b611c26565b611bca565b611bad565b611b56565b611b3c565b611b1f565b611a9a565b611a02565b61185d565b61183b565b6117b9565b611792565b6116ed565b6116d0565b6116ab565b61146a565b611448565b611403565b6113c8565b61139c565b61137f565b611365565b611344565b61131d565b6111f4565b611167565b61113f565b611124565b611067565b61104a565b610f50565b610e8a565b610e70565b610e07565b610d22565b610d02565b610cde565b610cb6565b610c57565b610c15565b610abb565b610a2f565b61097c565b61095f565b610908565b6108eb565b610832565b6107b0565b610793565b610742565b6106b6565b610688565b6105ae565b5f9103126104f357565b5f80fd5b90600182811c92168015610525575b602083101461051157565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610506565b634e487b7160e01b5f52604160045260245ffd5b602081019081106001600160401b0382111761055e57604052565b61052f565b90601f801991011681019081106001600160401b0382111761055e57604052565b602060409281835280519182918282860152018484015e5f828201840152601f01601f1916010190565b346104f3575f3660031901126104f3576040515f6001546105ce816104f7565b80845290600181169081156106645750600114610606575b610602836105f681850382610563565b60405191829182610584565b0390f35b60015f9081527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6939250905b80821061064a575090915081016020016105f66105e6565b919260018160209254838588010152019101909291610632565b60ff191660208086019190915291151560051b840190910191506105f690506105e6565b346104f3575f3660031901126104f3576020601654604051908152f35b6001600160a01b038116036104f357565b346104f35760403660031901126104f3576004356106d3816106a5565b60243590335f52600f602052816106fd8260405f209060018060a01b03165f5260205260405f2090565b556040519182526001600160a01b03169033907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a360405160018152602090f35b346104f35760203660031901126104f35761077d6004355f5461076760ff8216612b5e565b60ff19165f5561077561383f565b503333614439565b50600160ff195f5416175f5560206040515f8152f35b346104f3575f3660031901126104f3576020601254604051908152f35b346104f3575f3660031901126104f35760035460081c6001600160a01b031633036107d757005b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79207468652061646d696e206d61792063616c6c205f72657369676e4960448201526c36b83632b6b2b73a30ba34b7b760991b6064820152608490fd5b346104f35760203660031901126104f35760043561085e60018060a01b0360035460081c163314612b25565b670de0b6b3a764000081116108b5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef9060165461089b82601655565b604080519182526020820192909252a16108b3613d26565b005b60405162461bcd60e51b815260206004820152600e60248201526d34b73b30b634b210313ab33332b960911b6044820152606490fd5b346104f3575f3660031901126104f3576020600854604051908152f35b346104f35760203660031901126104f357602061094c60043561092a816106a5565b5f5461093860ff8216612b5e565b60ff19165f5561094661383f565b506140ef565b600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f3576020600d54604051908152f35b346104f3575f3660031901126104f3576020610996613d83565b604051908152f35b604435906109ab826106a5565b565b604051906109ab602083610563565b9291926001600160401b03821161055e57604051916109e5601f8201601f191660200184610563565b8294818452818301116104f3578281602093845f960137010152565b9080601f830112156104f357816020610a1c933591016109bc565b90565b60c4359060ff821682036104f357565b346104f35760e03660031901126104f357600435610a4c816106a5565b60243590610a59826106a5565b60443591610a66836106a5565b6064356084356001600160401b0381116104f357610a88903690600401610a01565b9060a435946001600160401b0386116104f357610aac6108b3963690600401610a01565b93610ab5610a1f565b95612bb1565b346104f35760203660031901126104f357600435610ad8816106a5565b60035460081c6001600160a01b031690610afc6001600160a01b0383163314612c36565b6013546001600160a01b039182169291610b3091610b289160089190911c165b6001600160a01b031690565b831415612c9a565b6040516370a0823160e01b8152306004820152602081602481865afa908115610bb5575f91610bba575b50823b156104f35760405163a9059cbb60e01b81526001600160a01b0390921660048301526024820152905f908290818381604481015b03925af18015610bb557610ba157005b80610baf5f6108b393610563565b806104e9565b612ba6565b610bdc915060203d602011610be2575b610bd48183610563565b810190612b97565b5f610b5a565b503d610bca565b60609060031901126104f357600435610c01816106a5565b90602435610c0e816106a5565b9060443590565b346104f3576020610c43610c2836610be9565b915f54610c3760ff8216612b5e565b60ff19165f5533613ddf565b15600160ff195f5416175f55604051908152f35b346104f35760403660031901126104f357610c9e600435610c77816106a5565b602435905f54610c8960ff8216612b5e565b60ff19165f55610c9761383f565b5033614439565b505f805460ff19166001178155604051908152602090f35b346104f3575f3660031901126104f3576004546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f3576020610cf8612d01565b6040519015158152f35b346104f3575f3660031901126104f357602060ff60035416604051908152f35b346104f35760403660031901126104f357600435610d3f816106a5565b60243590610d5b60018060a01b0360035460081c163314612b25565b6001600160a01b03168015610dd5577f18ee325de736e289ba66adeb00d343571c9a1fc3b44d1d202f4601b89b601bef91610db0610dd09260018060a01b03166001600160601b0360a01b6015541617601555565b610db981601655565b604051918291829190602060408401935f81520152565b0390a1005b60405162461bcd60e51b815260206004820152600a6024820152691e995c9bc81d985d5b1d60b21b6044820152606490fd5b346104f35760203660031901126104f3576020670de0b6b3a7640000610e66600435610e32816106a5565b610e3a6139b5565b9060405191610e4883610543565b82526001600160a01b03165f908152600e85526040902054906141fa565b5104604051908152f35b346104f3575f3660031901126104f3576020610996613fbc565b346104f35760203660031901126104f3576004355f54610eac60ff8216612b5e565b60ff19165f55610eba61383f565b506009544303610f3d57610ece90336149da565b600c54818101809111610f3857600c8190556040805133815260208101939093528201527fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc59080606081015b0390a1610f2d600160ff195f5416175f55565b6040515f8152602090f35b61304e565b6338acf79960e01b5f525f60045260245ffd5b346104f35760203660031901126104f357600435600354610f8790610f809060081c6001600160a01b0316610b1c565b3314612b25565b610f9360185415612d12565b601554610fa8906001600160a01b0316610b1c565b604051633ea0e43760e11b815260048101839052306024820181905260448201529190602090839060649082905f905af18015610bb5577fd5ba0459265c0cfa94aa9d715cc0b5c1305e25f72d371f73e627359065e83fdc925f9161102b575b5061101281601855565b6040805191825260208201929092529081908101610dd0565b611044915060203d602011610be257610bd48183610563565b5f611008565b346104f3575f3660031901126104f3576020600b54604051908152f35b346104f35760203660031901126104f3576004356001600160401b0381116104f357366023820112156104f3576110a89036906024816004013591016109bc565b6110c060018060a01b0360035460081c163314612b25565b8051806110c957005b81604091810103126104f35760406020820151916110e6836106a5565b01516001600160a01b0390911690816110fb57005b61111f6108b39260018060a01b03166001600160601b0360a01b6015541617601555565b601655565b346104f3575f3660031901126104f357602060405160018152f35b346104f3575f3660031901126104f3576014546040516001600160a01b039091168152602090f35b346104f35760803660031901126104f357600435611184816106a5565b60243590611191826106a5565b604435606435916001600160401b0383116104f357366023840112156104f3578260040135916001600160401b0383116104f35736602484860101116104f3576106029460246111e2950192612d53565b60405190151581529081906020820190565b346104f35760203660031901126104f3576004355f5461121660ff8216612b5e565b60ff19165f5561122461383f565b5060035461123d9060081c6001600160a01b0316610b1c565b330361130e5760095443036112ff5780611255613fbc565b106112f057600c54908181116112e157611290817f3bad0c59cf2f06e7314077049f48a93578cd16f5ef92329f1dab1420a99c177e93613186565b61129981600c55565b6003546112b390839060081c6001600160a01b0316613fd8565b6003546040805160089290921c6001600160a01b031682526020820193909352918201528060608101610f1a565b6378d2980560e11b5f5260045ffd5b633345e99960e01b5f5260045ffd5b630dff50cb60e41b5f5260045ffd5b630f7e5e6d60e41b5f5260045ffd5b346104f35760203660031901126104f357602061099660043561133f816106a5565b6131a0565b346104f3575f3660031901126104f3576020604051666379da05b600008152f35b346104f3575f3660031901126104f35760206040515f8152f35b346104f3575f3660031901126104f3576020600954604051908152f35b346104f3575f3660031901126104f35760135460405160089190911c6001600160a01b03168152602090f35b346104f35760203660031901126104f3576004356113e5816106a5565b60018060a01b03165f52600e602052602060405f2054604051908152f35b346104f3575f3660031901126104f3575f5461142160ff8216612b5e565b60ff19165f5561142f61383f565b506020600b54600160ff195f5416175f55604051908152f35b346104f3575f3660031901126104f357602060ff601754166040519015158152f35b346104f35760203660031901126104f3576004355f5461148c60ff8216612b5e565b60ff19165f5561149a61383f565b506114a5600161482d565b6114c16114b0613d83565b6114b86109ad565b90815282614beb565b6005549091906114d9906001600160a01b0316610b1c565b6020604051809263eabe7d9160e01b8252815f816114fc89333060048501614896565b03925af1908115610bb5575f91611682575b5080611670575060095443036116615780611527613fbc565b106116525761154061153b83600d54613186565b600d55565b335f908152600e6020526040902061155b9083905b54613186565b335f908152600e60205260409020556115748133613fd8565b604051828152309033905f516020614cbc5f395f51905f5290602090a360408051338152602081018390529081018390527fe5b754fb1abb7f01b499791d0b820ae3b6af3424ac1c59768edb53f4ec31a92990606090a16005546115e0906001600160a01b0316610b1c565b91823b156104f3576040516351dff98960e01b815230600482015233602482015260448101929092526064820152905f908290608490829084905af18015610bb55761163e575b5f805460ff19166001178155604051908152602090f35b80610baf5f61164c93610563565b5f611627565b6391240a1b60e01b5f5260045ffd5b6397b5cfcd60e01b5f5260045ffd5b635bd5f7ab60e11b5f5260045260245ffd5b61169b915060203d602011610be257610bd48183610563565b5f61150e565b801515036104f357565b346104f35760203660031901126104f3576108b36004356116cb816116a1565b6131e1565b346104f3575f3660031901126104f3576020600c54604051908152f35b346104f3575f3660031901126104f3576040515f60025461170d816104f7565b8084529060018116908115610664575060011461173457610602836105f681850382610563565b60025f9081527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace939250905b808210611778575090915081016020016105f66105e6565b919260018160209254838588010152019101909291611760565b346104f35760203660031901126104f35760206109966004356117b4816106a5565b6140ef565b346104f35760c03660031901126104f3576004356117d6816106a5565b6024356117e2816106a5565b6044356064356001600160401b0381116104f357611804903690600401610a01565b6084356001600160401b0381116104f357611823903690600401610a01565b9160a4359360ff851685036104f3576108b3956135b4565b346104f3575f3660031901126104f357602060ff601354166040519015158152f35b346104f35760203660031901126104f3576004355f5461187f60ff8216612b5e565b60ff19165f5561188d61383f565b506005546118a3906001600160a01b0316610b1c565b60206040518092634ef4c3e160e01b8252815f816118c688333060048501614896565b03925af1908115610bb5575f916119d6575b50806119c4575060095443036119b5577f4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f611930611929611917613d83565b936119206109ad565b948552336149da565b9283614beb565b809261194161153b83600d54613193565b335f908152600e6020526040902061195c9083905b54613193565b335f908152600e602052604090205560408051338152602081019290925281019190915280606081010390a1604051908152339030905f516020614cbc5f395f51905f5290602090a3610f2d600160ff195f5416175f55565b6338d8859760e01b5f5260045ffd5b6303171a8760e61b5f5260045260245ffd5b6119ef915060203d602011610be257610bd48183610563565b5f6118d8565b60e435906109ab826106a5565b346104f3576101203660031901126104f357600435611a20816106a5565b60243590611a2d826106a5565b611a3561099e565b91606435926084356001600160401b0381116104f357611a59903690600401610a01565b9360a435946001600160401b0386116104f357611a7d6108b3963690600401610a01565b91611a86610a1f565b93611a8f6119f5565b9561010435976136ec565b346104f35760203660031901126104f357600435611ab7816106a5565b600354611ad19060081c6001600160a01b0316331461378e565b601354611aeb90610b1c9060081c6001600160a01b031681565b803b156104f3576040516317066a5760e21b81526001600160a01b0383166004820152905f90829081838160248101610b91565b346104f3575f3660031901126104f3576020601954604051908152f35b346104f3575f3660031901126104f357602061099661383f565b346104f35760403660031901126104f357611b95600435611b76816106a5565b602435905f54611b8860ff8216612b5e565b60ff19165f553380613ddf565b5f805460ff1916600117905560405190158152602090f35b346104f3575f3660031901126104f3576020600a54604051908152f35b346104f35760203660031901126104f3577f66f1d115ba8b08c31c22d86374bc36cea7c2771ef78949fa59328010115b4b9c6020600435611c1960018060a01b0360035460081c163314612b25565b80601955604051908152a1005b346104f3575f3660031901126104f3576006546001600160a01b03166020611c4c613fbc565b600b54600c54600854604051635c0b440b60e11b815260048101949094526024840192909252604483015260648201529182908180608481015b03915afa8015610bb557610602915f91611cac575b506040519081529081906020820190565b611cc5915060203d602011610be257610bd48183610563565b5f611c9b565b346104f357611cf7611cdc36610be9565b915f54611ceb60ff8216612b5e565b60ff19165f5533614225565b600160ff195f5416175f5560206040515f8152f35b346104f35760203660031901126104f357600435611d29816106a5565b60035460081c6001600160a01b03163303611da557600480546001600160a01b039283166001600160a01b0319821681179092556040805193909116835260208301919091527fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a99190819081015b0390a16040515f8152602090f35b635cb56c2b60e01b5f5260045ffd5b346104f3575f3660031901126104f35760206109966139b5565b346104f3575f3660031901126104f3576020601854604051908152f35b346104f3575f3660031901126104f3576020601154604051908152f35b346104f35760203660031901126104f3576080600435611e27816106a5565b6001600160a01b0381165f908152600e602052604090205490611e49906140ef565b611e51613d83565b90604051925f8452602084015260408301526060820152f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611ee85780611ec36127107f84e3f33c20d693ca1e98ee0ab87bf817997a96baa0819ed94c5b96f4a6663f139311156139e8565b601254611ecf82601255565b6040805191825260208201929092529081908101611d97565b60405162461bcd60e51b815260206004820152602760248201527f4f6e6c792061646d696e2063616e20736574206d617820666c617368206c6f616044820152666e20726174696f60c81b6064820152608490fd5b346104f3575f3660031901126104f3576005546040516001600160a01b039091168152602090f35b346104f35760203660031901126104f35760043560035460081c6001600160a01b03163303611fc95780611fbd60647f2cd87d6d243502fbf87bd7dd851659e05ac6a614994d7f04592e4f173f939783931115613a34565b601154611ecf82601155565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c792061646d696e2063616e2073657420666c617368206c6f616e2066656044820152606560f81b6064820152608490fd5b346104f35760203660031901126104f3576004355f5461203a60ff8216612b5e565b60ff19165f5561204861383f565b5060055461205e906001600160a01b0316610b1c565b6020604051809263368f515360e21b8252815f8161208188333060048501614896565b03925af1908115610bb5575f91612175575b50806121635750600954430361215457806120ac613fbc565b1061214557806120e57f13ed6866d4e1ee6da46f845c46d7e54120883d75c5ea9a2dacc1c4ca8984ab80926120e0336140ef565b613193565b90610f1a6120f582600b54613193565b335f908152601060205260409020848155600a54600190910155600b81905561211e8333613fd8565b60408051338152602081019490945283019390935260608201929092529081906080820190565b6348c2588160e01b5f5260045ffd5b630e8d8c6160e21b5f5260045ffd5b635684deff60e11b5f5260045260245ffd5b61218e915060203d602011610be257610bd48183610563565b5f612093565b346104f35760203660031901126104f35760206109966004356121b6816106a5565b613ae1565b346104f35760403660031901126104f3576127106122086004356121de816106a5565b601354602435916121ff9160081c6001600160a01b03908116911614612f56565b60115490613062565b6040519190048152602090f35b346104f35760203660031901126104f3576004355f5461223760ff8216612b5e565b60ff19165f5561224561383f565b50801590811591826122b7575b61225b9061482d565b612263613d83565b9161226c6109ad565b928352156122a6576122828161229192936141fa565b670de0b6b3a764000090510490565b6005546114d9906001600160a01b0316610b1c565b506122b1905f614beb565b5f612291565b506001612252565b346104f35760203660031901126104f3576020610cf86004356122e1816116a1565b613bf8565b346104f35760403660031901126104f357602061233c600435612308816106a5565b60243590612315826106a5565b60018060a01b03165f52600f835260405f209060018060a01b03165f5260205260405f2090565b54604051908152f35b346104f3575f3660031901126104f3576004546001600160a01b03168033811480159061245e575b61244f576003547fca4f2f25d0898edd99413412fb94012f9e54ec8142f9b093e7720646a95b16a9927ff9ffabca9c8276e99321725bcb43fb076a6c66a54b7f21c4e8146d8519b417dc916123f6906123d19060081c6001600160a01b0316610b1c565b91600354610100600160a81b03191660089190911b610100600160a81b031617600355565b600480546001600160a01b0319169055600354604080516001600160a01b03938416815260089290921c9092166020820152a1600454604080516001600160a01b03938416815292909116602083015281908101611d97565b631ba24f2960e21b5f5260045ffd5b50331561236d565b346104f3575f3660031901126104f35760035461249290610f809060081c6001600160a01b0316610b1c565b6018546124a0811515613ce2565b6015546124b5906001600160a01b0316610b1c565b604051635d043b2960e11b815260048101839052306024820181905260448201529190602090839060649082905f905af1908115610bb5577fc220b62a9bc2190b38c6c59a97aeca3788b99c20f6ba14f5f02b144a1d79dd43925f92612520575b5061089b5f601855565b61253a91925060203d602011610be257610bd48183610563565b905f612516565b346104f35760203660031901126104f3576020610996600435612563816106a5565b61256b61383f565b5061412f565b346104f3575f3660031901126104f3576006546040516001600160a01b039091168152602090f35b346104f35760603660031901126104f3576004356125b6816106a5565b602435906044356125c6816106a5565b5f546125d460ff8216612b5e565b60ff19165f556125e261383f565b5060405163a6afed9560e01b81526001600160a01b039190911692906020816004815f885af1908115610bb5575f9161297e575b508061296c5750600554612632906001600160a01b0316610b1c565b604051632fe3f38f60e11b81523060048201526001600160a01b038581166024830152336044830152841660648201526084810183905290602090829060a49082905f905af1908115610bb5575f9161294d575b508061293b5750600954430361292c57604051636c540baf60e01b8152602081600481875afa908115610bb5575f9161290d575b5043036128fe576001600160a01b03821633146128ef5780156128e0575f1981146128d1576126ea908233614439565b600554909190612702906001600160a01b0316610b1c565b6040805163c488847b60e01b815294918590818061272588873060048501614896565b03915afa938415610bb5575f905f9561289d575b5061274490156148e3565b6040516370a0823160e01b81526001600160a01b0383166004820152602081602481855afa908115610bb5576127859186915f9161287e575b50101561494b565b3081036127f55783610f1a916127be7f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb5296853330614225565b604080513381526001600160a01b0395861660208201529081019590955290921660608401526080830191909152819060a0820190565b60405163b2a02ff160e01b8152936020858061281684873360048501614896565b03815f865af1918215610bb55761285a610f1a937f298637f684da70674f26509b10f07ec2fbc77a335ab1e7d6215a4b2484d8bb52975f9161285f575b5015614997565b6127be565b612878915060203d602011610be257610bd48183610563565b5f612853565b612897915060203d602011610be257610bd48183610563565b5f61277d565b61274495506128c4915060403d6040116128ca575b6128bc8183610563565b8101906148cd565b94612739565b503d6128b2565b635982c5bb60e11b5f5260045ffd5b63d29da7ef60e01b5f5260045ffd5b631bd1a62160e21b5f5260045ffd5b631046f38d60e31b5f5260045ffd5b612926915060203d602011610be257610bd48183610563565b5f6126ba565b6380965b1b60e01b5f5260045ffd5b632d33e4f360e21b5f5260045260245ffd5b612966915060203d602011610be257610bd48183610563565b5f612686565b633eea49b760e11b5f5260045260245ffd5b612997915060203d602011610be257610bd48183610563565b5f612616565b346104f3575f3660031901126104f3576015546040516001600160a01b039091168152602090f35b346104f3575f3660031901126104f35760035460405160089190911c6001600160a01b03168152602090f35b346104f3575f3660031901126104f357600654611c86906020906001600160a01b0316612a1c613fbc565b600b54600c546040516315f2405360e01b815260048101939093526024830191909152604482015292839190829081906064820190565b346104f35760203660031901126104f3576004355f54612a7560ff8216612b5e565b60ff19165f55612a8361383f565b5060035460081c6001600160a01b03163303612b16576009544303612b0757670de0b6b3a76400008111612af8577faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146090600854612adf82600855565b6040805191825260208201929092529081908101610f1a565b63717220f360e11b5f5260045ffd5b637dfca6b760e11b5f5260045ffd5b631205b57b60e11b5f5260045ffd5b15612b2c57565b60405162461bcd60e51b815260206004820152600a60248201526937b7363c9030b236b4b760b11b6044820152606490fd5b15612b6557565b60405162461bcd60e51b815260206004820152600a6024820152691c994b595b9d195c995960b21b6044820152606490fd5b908160209103126104f3575190565b6040513d5f823e3d90fd5b9294612be994602096612bc794600499946135b4565b601354610100600160a81b03191660089190911b610100600160a81b03161790565b60138190556040516318160ddd60e01b8152928391829060081c6001600160a01b03165afa8015610bb557612c1b5750565b612c339060203d602011610be257610bd48183610563565b50565b15612c3d57565b60405162461bcd60e51b815260206004820152602f60248201527f5045726332303a3a7377656570546f6b656e3a206f6e6c792061646d696e206360448201526e616e20737765657020746f6b656e7360881b6064820152608490fd5b15612ca157565b60405162461bcd60e51b815260206004820152603260248201527f5045726332303a3a7377656570546f6b656e3a2063616e206e6f74207377656560448201527138103ab73232b9363cb4b733903a37b5b2b760711b6064820152608490fd5b60185415612d0e57600190565b5f90565b15612d1957565b60405162461bcd60e51b8152602060048201526012602482015271726564656d7074696f6e2070656e64696e6760701b6044820152606490fd5b915f54612d6260ff8216612b5e565b60ff19165f55601354612d809060081c6001600160a01b0316610b1c565b6001600160a01b0383169590612d97908714612f56565b612db2612dad612da960135460ff1690565b1590565b612fa2565b60405163613255ab60e01b81526001600160a01b0384166004820152602081602481305afa8015610bb557612e4d94612df985926020945f91612f39575b50831115612ff8565b612e10612e0860115484613062565b612710900490565b958694612e1b613fbc565b6001600160a01b0390991699612e31858c613fd8565b6040516323e30c8b60e01b815296879586953360048801613093565b03815f895af1908115610bb5577f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f094612eb67f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9612ed894612ecf945f91612f1a575b50146130e3565b612ebe613fbc565b612eca8282101561313a565b613186565b9283101561313a565b81612f01575b604080519182526020820192909252a36001906109ab600160ff195f5416175f55565b612f15612f1083600c54613193565b600c55565b612ede565b612f33915060203d602011610be257610bd48183610563565b5f612eaf565b612f509150853d8711610be257610bd48183610563565b5f612df0565b15612f5d57565b60405162461bcd60e51b815260206004820152601e60248201527f466c6173684c6f616e3a20746f6b656e206e6f7420737570706f7274656400006044820152606490fd5b15612fa957565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20666c617368206c6f616e73206172652070617573656044820152601960fa1b6064820152608490fd5b15612fff57565b60405162461bcd60e51b815260206004820152602160248201527f466c6173684c6f616e3a20616d6f756e742065786365656473206d6178696d756044820152606d60f81b6064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b81810292918115918404141715610f3857565b811561307f570490565b634e487b7160e01b5f52601260045260245ffd5b6001600160a01b039182168152911660208201526040810191909152606081019190915260a060808201819052810183905260c09290918190848401375f828201840152601f01601f1916010190565b156130ea57565b60405162461bcd60e51b815260206004820152602260248201527f466c6173684c6f616e3a20696e76616c69642063616c6c6261636b2072657475604482015261393760f11b6064820152608490fd5b1561314157565b60405162461bcd60e51b815260206004820152601a60248201527f466c6173684c6f616e3a206c6f616e206e6f74207265706169640000000000006044820152606490fd5b91908203918211610f3857565b91908201809211610f3857565b601354906001600160a01b03600883901c81169116036131dc5760ff16612d0e576127106131d86131cf613fbc565b60125490613062565b0490565b505f90565b6131f960018060a01b0360035460081c163314612b25565b60175490151560ff8216151581146132475760ff1990911660ff8216176017556040519081527fe99ce3808ef0a5bbecc8824375b67638e945a147d2836eaf2a2c2b8f482bb54e90602090a1565b5050565b1561325257565b60405162461bcd60e51b815260206004820152602360248201527f6d61726b6574206d6179206f6e6c7920626520696e697469616c697a6564206f6044820152626e636560e81b6064820152608490fd5b156132aa57565b60405162461bcd60e51b815260206004820152603060248201527f696e697469616c2065786368616e67652072617465206d75737420626520677260448201526f32b0ba32b9103a3430b7103d32b9379760811b6064820152608490fd5b1561330f57565b60405162461bcd60e51b815260206004820152601d60248201527f73657474696e672070657269646f7474726f6c6c6572206661696c65640000006044820152606490fd5b1561335b57565b60405162461bcd60e51b815260206004820152602260248201527f73657474696e6720696e7465726573742072617465206d6f64656c206661696c604482015261195960f21b6064820152608490fd5b601f82116133b857505050565b5f5260205f20906020601f840160051c830193106133f0575b601f0160051c01905b8181106133e5575050565b5f81556001016133da565b90915081906133d1565b9081516001600160401b03811161055e576134218161341a6001546104f7565b60016133ab565b602092601f821160011461346157613451929382915f92613456575b50508160011b915f199060031b1c19161790565b600155565b015190505f8061343d565b60015f52601f198216937fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f5b8681106134c557508360019596106134ad575b505050811b01600155565b01515f1960f88460031b161c191690555f80806134a2565b9192602060018192868501518155019401920161348f565b9081516001600160401b03811161055e57613504816134fd6002546104f7565b60026133ab565b602092601f821160011461353857613533929382915f926134565750508160011b915f199060031b1c19161790565b600255565b60025f52601f198216937f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f5b86811061359c5750836001959610613584575b505050811b01600255565b01515f1960f88460031b161c191690555f8080613579565b91926020600181928685015181550194019201613566565b60035490959492939060081c6001600160a01b03166001600160a01b0316330361369b5761364361363d6136489361361e61361861365d9a61360b61364d9a6009541580613691575b6136069061324b565b600755565b6121b660075415156132a3565b15613308565b61362743600955565b613638670de0b6b3a7640000600a55565b61412f565b15613354565b6133fa565b6134dd565b60ff1660ff196003541617600355565b6136676005601155565b613672610bb8601255565b61368160ff1960135416601355565b6109ab600160ff195f5416175f55565b50600a54156135fd565b60405162461bcd60e51b8152602060048201526024808201527f6f6e6c792061646d696e206d617920696e697469616c697a6520746865206d616044820152631c9ad95d60e21b6064820152608490fd5b90919293949596976009541580613784575b15613750576003546109ab9961374891610100600160a81b0319163360081b610100600160a81b03161760035560018060a01b03166001600160601b0360a01b6015541617601555565b601655612bb1565b60405162461bcd60e51b815260206004820152600c60248201526b185b1c9958591e481a5b9a5d60a21b6044820152606490fd5b50600a54156136fe565b1561379557565b60405162461bcd60e51b815260206004820152603060248201527f6f6e6c79207468652061646d696e206d6179207365742074686520706572696460448201526f6f742d6c696b652064656c656761746560801b6064820152608490fd5b156137fa57565b60405162461bcd60e51b815260206004820152601c60248201527f626f72726f772072617465206973206162737572646c792068696768000000006044820152606490fd5b6009544381146131dc57613851613fbc565b600b5490600c5492600a5492613871610b1c60065460018060a01b031690565b6040516315f2405360e01b81526004810185905260248101839052604481018790529590602090879060649082905afa958615610bb55761394a866120e06122826139389961394461391b7f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc049d6139899b613966995f92613990575b5061390a9061390465048c273950008411156137f3565b43613186565b906139136109ad565b9081526141fa565b976120e061228261392f6122828d8d6141fa565b9e8f9c8d613193565b9b6008546139136109ad565b966141fa565b9161395443600955565b61395d83600a55565b612f1084600b55565b604051948594859094939260609260808301968352602083015260408201520152565b0390a15f90565b61390a9192506139ae9060203d602011610be257610bd48183610563565b91906138ed565b5f546139c360ff8216612b5e565b60ff19165f556139d161383f565b506139da613d83565b90600160ff195f5416175f55565b156139ef57565b60405162461bcd60e51b815260206004820152601960248201527f466c617368206c6f616e20726174696f20746f6f2068696768000000000000006044820152606490fd5b15613a3b57565b60405162461bcd60e51b815260206004820152601760248201527f466c617368206c6f616e2066656520746f6f20686967680000000000000000006044820152606490fd5b908160209103126104f35751610a1c816116a1565b15613a9c57565b60405162461bcd60e51b815260206004820152601c60248201527f6d61726b6572206d6574686f642072657475726e65642066616c7365000000006044820152606490fd5b600354613af99060081c6001600160a01b0316610b1c565b3303613bd4576005546001600160a01b031660405163fa707af960e01b815291906020836004816001600160a01b0386165afa928315610bb5577ff911792cf103798503b6dab0992e023d45661865683c550a919b8c9ad07dbf7f93613b66915f91613ba5575b50613a95565b600580546001600160a01b0319166001600160a01b0384161790555b604080516001600160a01b03928316815292909116602083015281908101613989565b613bc7915060203d602011613bcd575b613bbf8183610563565b810190613a80565b5f613b60565b503d613bb5565b634c2fa00360e01b5f5260045ffd5b908160209103126104f35751610a1c816106a5565b600354613c109060081c6001600160a01b0316610b1c565b33148015613c61575b156131dc57613c59816111e27f7958d1d41a26870ec9486cb4dc33830b446f038d00661e064b50719bdde7f42c9360ff8019601354169115151617601355565b0390a1600190565b5060046020613c80610b1c610b1c610b1c60055460018060a01b031690565b604051631251eb1160e11b815292839182905afa908115610bb5575f91613cb3575b506001600160a01b03163314613c19565b613cd5915060203d602011613cdb575b613ccd8183610563565b810190613be3565b5f613ca2565b503d613cc3565b15613ce957565b60405162461bcd60e51b81526020600482015260156024820152743737903832b73234b733903932b232b6b83a34b7b760591b6044820152606490fd5b60ff601754166109ab57613d386145da565b613d40614633565b8101808211610f3857801561324757613d65670de0b6b3a76400009160165490613062565b04808211613d71575050565b8103908111610f38576109ab906146cd565b600d5480613d92575060075490565b613d9a613fbc565b90600b548201809211610f3857600c549182810392818411610f3857670de0b6b3a76400008402938404670de0b6b3a76400001491141715610f3857610a1c91613075565b600554613df4906001600160a01b0316610b1c565b6040516317b9b84b60e31b81523060048201526001600160a01b038481166024830152851660448201526064810186905290602090829060849082905f905af1908115610bb5575f91613f9d575b5080613f8b57506001600160a01b038381169490831693848614613f7c575f516020614cbc5f395f51905f5293613f12936001600160a01b0381168703613f54575f195b613e908582613186565b93613ef8613eb2876115558760018060a01b03165f52600e60205260405f2090565b91613ed1886119568360018060a01b03165f52600e60205260405f2090565b6001600160a01b038781165f908152600e6020526040808220969096559216825292902090565b5519613f19575b5050604051918252509081906020820190565b0390a35f90565b613f36613f4b9260018060a01b03165f52600f60205260405f2090565b9060018060a01b03165f5260205260405f2090565b555f8080613eff565b6001600160a01b0382165f908152600f60205260409020613f76908290613f36565b54613e86565b638cd22d1960e01b5f5260045ffd5b630c93cb5b60e01b5f5260045260245ffd5b613fb6915060203d602011610be257610bd48183610563565b5f613e42565b613fc46145da565b613fcc614633565b8101809111610f385790565b81613fe16145da565b1061408d5760135461400090610b1c9060081c6001600160a01b031681565b91823b156104f35760405163a9059cbb60e01b81526001600160a01b039290921660048301526024820152905f908290604490829084905af18015610bb557614079575b503d801561406e57602014614057575f80fd5b60205f803e6140665f516147e1565b6109ab613d26565b506140665f196147e1565b80610baf5f61408793610563565b5f614044565b60405162461bcd60e51b815260206004820152603460248201527f696e73756666696369656e7420627566666572202d2061646d696e206d7573746044820152731031b7b6b83632ba32903932b232b6b83a34b7b760611b6064820152608490fd5b6001600160a01b03165f9081526010602052604090208054908115614129576001614120610a1c93600a5490613062565b91015490613075565b50505f90565b6003546141479060081c6001600160a01b0316610b1c565b33036141eb5760095443036141dc576006546001600160a01b03166040516310c8fc9560e11b815291906020836004816001600160a01b0386165afa928315610bb5577fedffc32e068c7c95dfd4bdfd5c4d939a084d6b11c4199eac8436ed234d72f926936141bc915f91613ba55750613a95565b600680546001600160a01b0319166001600160a01b038416179055613b82565b630be2a5cb60e11b5f5260045ffd5b63407fded560e01b5f5260045ffd5b90614213915f60405161420c81610543565b5251613062565b6040519061422082610543565b815290565b92602083829395865f614242610b1c60055460018060a01b031690565b60405163d02f735160e01b81523060048201526001600160a01b03948516602482015292841660448401529390921660648201526084810194909452839160a49183915af1908115610bb5575f9161441a575b508061440857506001600160a01b03838116939083168085146143f9577fa91e67c5ea634cd43a12c5a482724b03de01e85ca68702a53d0c2f45cb7c1dc594815f516020614cbc5f395f51905f526143bb614342976143056142f56109ad565b666379da05b600008152896148b8565b96614310888a613186565b9161438861436d61432e6122828c614326613d83565b6139136109ad565b9b61433b8d600c54613193565b9d8e600c55565b61435161153b8d600d54613186565b6001600160a01b0384165f908152600e60205260409020611555565b6001600160a01b039092165f908152600e6020526040902090565b556143aa61436d836119568460018060a01b03165f52600e60205260405f2090565b556040519081529081906020820190565b0390a360405191825230915f516020614cbc5f395f51905f5290602090a360408051308152602081019290925281019190915280606081015b0390a1565b633a94626760e11b5f5260045ffd5b6305a647b960e31b5f5260045260245ffd5b614433915060203d602011610be257610bd48183610563565b5f614295565b600554909190614451906001600160a01b0316610b1c565b604051631200453160e11b81523060048201526001600160a01b038481166024830152831660448201526064810185905290602090829060849082905f905af1908115610bb5575f916145bb575b50806145a95750600954430361459a577f1a2a22cb034d26d1854bdc6666a5b91fe25efbbb5dcad3b0355478d6f5c362a1916144da826140ef565b935f19810361458b57506145856144fc6144f5865b846149da565b8096613186565b61450886600b54613186565b90806145248660018060a01b03165f52601060205260405f2090565b55600a546001600160a01b0386165f9081526010602052604090206001015561454c82600b55565b604080516001600160a01b03958616815295909416602086015292840186905260608401929092526080830191909152819060a0820190565b0390a190565b6144fc6144f5614585926144ef565b63c9021e2f60e01b5f5260045ffd5b6335a6a70b60e21b5f5260045260245ffd5b6145d4915060203d602011610be257610bd48183610563565b5f61449f565b6013546040516370a0823160e01b8152306004820152906020908290602490829060081c6001600160a01b03165afa908115610bb5575f9161461a575090565b610a1c915060203d602011610be257610bd48183610563565b601554614648906001600160a01b0316610b1c565b6040516370a0823160e01b815230600482015290602082602481845afa918215610bb5575f926146ac575b508115614129576040516303d1689d60e11b81526004810192909252602090829060249082905afa908115610bb5575f9161461a575090565b6146c691925060203d602011610be257610bd48183610563565b905f614673565b801580156147d5575b612c33576013545f91906147169082906146fb9060081c6001600160a01b0316610b1c565b601554614710906001600160a01b0316610b1c565b90614b16565b60155460209061476690614732906001600160a01b0316610b1c565b6019546040516364ee50b160e01b815260048101869052306024820152604481019190915294859283919082906064820190565b03925af1908115610bb5577f6c89b3dfee140ff66a50284d3adbd08d9d66970bf92bfbbb60e8ed1dda9dec04925f926147b4575b5060408051918252602082019290925290819081016143f4565b6147ce91925060203d602011610be257610bd48183610563565b905f61479a565b5060175460ff166146d6565b156147e857565b60405162461bcd60e51b815260206004820152601960248201527f544f4b454e5f5452414e534645525f4f55545f4641494c4544000000000000006044820152606490fd5b1561483457565b60405162461bcd60e51b815260206004820152603460248201527f6f6e65206f662072656465656d546f6b656e73496e206f722072656465656d416044820152736d6f756e74496e206d757374206265207a65726f60601b6064820152608490fd5b6001600160a01b03918216815291166020820152604081019190915260600190565b6131d890670de0b6b3a7640000925190613062565b91908260409103126104f3576020825192015190565b156148ea57565b60405162461bcd60e51b815260206004820152603360248201527f4c49515549444154455f434f4d5054524f4c4c45525f43414c43554c4154455f604482015272105353d5539517d4d152569157d19052531151606a1b6064820152608490fd5b1561495257565b60405162461bcd60e51b815260206004820152601860248201527f4c49515549444154455f5345495a455f544f4f5f4d55434800000000000000006044820152606490fd5b1561499e57565b60405162461bcd60e51b81526020600482015260146024820152731d1bdad95b881cd95a5e9d5c994819985a5b195960621b6044820152606490fd5b6013546040516370a0823160e01b815230600482015260089190911c6001600160a01b0316929091602083602481875afa928315610bb5575f93614af5575b50833b156104f3576040516323b872dd60e01b8152915f9183918291614a459190309060048501614896565b038183875af18015610bb557614ae1575b503d8015614ad657602014614a69575f80fd5b60205f803e614a785f51614c17565b6040516370a0823160e01b815230600482015291602090839060249082905afa8015610bb557614aaf925f91614ab7575b50613186565b610a1c613d26565b614ad0915060203d602011610be257610bd48183610563565b5f614aa9565b50614a785f19614c17565b80610baf5f614aef93610563565b5f614a56565b614b0f91935060203d602011610be257610bd48183610563565b915f614a19565b60405163095ea7b360e01b60208281019182526001600160a01b0385166024840152604480840196909652948252929390925f90614b55606486610563565b84519082855af15f51903d81614bbf575b501590505b614b7457505050565b60405163095ea7b360e01b60208201526001600160a01b039390931660248401525f60448085019190915283526109ab92614bba90614bb4606482610563565b82614c63565b614c63565b15159050614bdf5750614b6b6001600160a01b0382163b15155b5f614b66565b6001614b6b9114614bd9565b670de0b6b3a7640000810290808204670de0b6b3a76400001490151715610f3857610a1c915190613075565b15614c1e57565b60405162461bcd60e51b815260206004820152601860248201527f544f4b454e5f5452414e534645525f494e5f4641494c454400000000000000006044820152606490fd5b905f602091828151910182855af115612ba6575f513d614cb257506001600160a01b0381163b155b614c925750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614c8b56feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122041851f2064eb0319c3b4be00ac29637f6834fd1a22c297eeb8f839c535abd4db64736f6c634300081e0033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.