MON Price: $0.021564 (+14.73%)

Contract

0x0db363a2eDB3Ac8F4CDdC174ff44B6dfd09A2FeF

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Update Dynamic I...393429472025-12-02 5:45:2556 days ago1764654325IN
0x0db363a2...fd09A2FeF
0 MON0.01964345102.01
Set Linked Token378333642025-11-25 4:53:5163 days ago1764046431IN
0x0db363a2...fd09A2FeF
0 MON0.00993154103.74

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

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

Contract Name:
DynamicIRM

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion, BSL 1.1 license

Contract Source Code (Solidity)

/**
 *Submitted for verification at monadscan.com on 2025-11-25
*/

// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.28;

// contracts/libraries/ConstantsLib.sol

/// @dev Scalar for math. `WAD` * `WAD`.
uint256 constant WAD_SQUARED = 1e36;

/// @dev Scalar for math. `WAD` * `WAD` / `BPS`.
///      1e18 * 1e18 / 1e4
uint256 constant WAD_SQUARED_BPS_OFFSET = 1e32;

/// @dev Scalar for math. Increased precision when WAD is insufficient
///      but WAD_SQUARED runs the risk of overflow.
uint256 constant RAY = 1e27;

/// @dev Scalar for math. `WAD` * `BPS`.
uint256 constant WAD_BPS = 1e22;

/// @dev Scalar for math. Base precision matching ether.
uint256 constant WAD = 1e18;

/// @dev Scalar for math. Represents basis points typically used in TradFi.
uint256 constant BPS = 1e4;

/// @dev Return value indicating no price returned at all.
uint256 constant BAD_SOURCE = 2;

/// @dev Return value indicating price divergence or a missing price feed.
uint256 constant CAUTION = 1;

/// @dev Return value indicating no price error.
uint256 constant NO_ERROR = 0;

/// @dev Extra time added to top end Oracle feed heartbeat incase of
///      transaction congestion delaying an update.
uint256 constant HEARTBEAT_GRACE_PERIOD = 120;

/// @dev Unix time has 31,536,000 seconds per year.
///      All my homies hate leap seconds and leap years.
uint256 constant SECONDS_PER_YEAR = 31_536_000;

// contracts/libraries/external/FixedPointMathLib.sol

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @dev Reduced function scope from full FixedPointMathLib library to only what is needed for Curvance.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                // 512-bit multiply `[p1 p0] = x * y`.
                // Compute the product mod `2**256` and mod `2**256 - 1`
                // then use the Chinese Remainder Theorem to reconstruct
                // the 512 bit result. The result is stored in two 256
                // variables such that `product = p1 * 2**256 + p0`.

                // Least significant 256 bits of the product.
                result := mul(x, y) // Temporarily use `result` as `p0` to save gas.
                let mm := mulmod(x, y, not(0))
                // Most significant 256 bits of the product.
                let p1 := sub(mm, add(result, lt(mm, result)))

                // Handle non-overflow cases, 256 by 256 division.
                if iszero(p1) {
                    if iszero(d) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    result := div(result, d)
                    break
                }

                // Make sure the result is less than `2**256`. Also prevents `d == 0`.
                if iszero(gt(d, p1)) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }

                /*------------------- 512 by 256 division --------------------*/

                // Make division exact by subtracting the remainder from `[p1 p0]`.
                // Compute remainder using mulmod.
                let r := mulmod(x, y, d)
                // `t` is the least significant bit of `d`.
                // Always greater or equal to 1.
                let t := and(d, sub(0, d))
                // Divide `d` by `t`, which is a power of two.
                d := div(d, t)
                // Invert `d mod 2**256`
                // Now that `d` is an odd number, it has an inverse
                // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                // Compute the inverse by starting with a seed that is correct
                // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                let inv := xor(2, mul(3, d))
                // Now use Newton-Raphson iteration to improve the precision.
                // Thanks to Hensel's lifting lemma, this also works in modular
                // arithmetic, doubling the correct bits in each step.
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                result :=
                    mul(
                        // Divide [p1 p0] by the factors of two.
                        // Shift in bits from `p1` into `p0`. For this we need
                        // to flip `t` such that it is `2**256 / t`.
                        or(
                            mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)),
                            div(sub(result, r), t)
                        ),
                        // inverse mod 2**256
                        mul(inv, sub(2, mul(d, inv)))
                    )
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        result = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                result := add(result, 1)
                if iszero(result) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, y), d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
        }
    }

    /// @dev Returns the square root of `x`.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }
}

// contracts/interfaces/ICentralRegistry.sol

/// TYPES ///

/// @notice Configuration data for a separately supported blockchain.
/// @param isSupported Whether the chain is supported or not.
/// @param messagingChainId Messaging Chain ID where this address authorized.
/// @param domain Domain for the chain.
/// @param messagingHub Messaging Hub address on the chain.
/// @param votingHub Voting Hub address on the chain.
/// @param cveAddress CVE address on the chain.
/// @param feeTokenAddress Fee token address on the chain.
/// @param crosschainRelayer Crosschain relayer address on the chain.
struct ChainConfig {
    bool isSupported;
    uint16 messagingChainId;
    uint32 domain;
    address messagingHub;
    address votingHub;
    address cveAddress;
    address feeTokenAddress;
    address crosschainRelayer;
}

interface ICentralRegistry {
    /// @notice The length of one protocol epoch, in seconds.
    function EPOCH_DURATION() external view returns (uint256);

    /// @notice Sequencer uptime oracle on this chain (for L2s).
    function SEQUENCER_ORACLE() external view returns (address);

    /// @notice Returns Genesis Epoch Timestamp of Curvance.
    function genesisEpoch() external view returns (uint256);

    /// @notice Returns Protocol DAO address.
    function daoAddress() external view returns (address);

    /// @notice Returns Protocol Emergency Council address.
    function emergencyCouncil() external view returns (address);

    /// @notice Indicates if address has DAO permissions or not.
    function hasDaoPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Indicates if address has elevated DAO permissions or not.
    function hasElevatedPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Indicates if address has lock creation permissions or not.
    function hasLockingPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Indicates if address has auction permissions or not.
    function hasAuctionPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Indicates if address has market permissions or not.
    function hasMarketPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Indicates if address has harvest permissions or not.
    function hasHarvestPermissions(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Returns Reward Manager address.
    function rewardManager() external view returns (address);

    /// @notice Returns Gauge Manager address.
    function gaugeManager() external view returns (address);

    /// @notice Returns CVE address.
    function cve() external view returns (address);

    /// @notice Returns veCVE address.
    function veCVE() external view returns (address);

    /// @notice Returns Voting Hub address.
    function votingHub() external view returns (address);

    /// @notice Returns Messaging Hub address.
    function messagingHub() external view returns (address);

    /// @notice Returns Oracle Manager address.
    function oracleManager() external view returns (address);

    /// @notice Returns Fee Manager address.
    function feeManager() external view returns (address);

    /// @notice Returns Fee Token address.
    function feeToken() external view returns (address);

    /// @notice Returns Crosschain Core contract address.
    function crosschainCore() external view returns (address);

    /// @notice Returns Crosschain Relayer contract address.
    function crosschainRelayer() external view returns (address);

    /// @notice Returns Token Messenger contract address.
    function tokenMessager() external view returns (address);

    /// @notice Returns Messenger Transmitter contract address.
    function messageTransmitter() external view returns (address);

    /// @notice Returns domain value.
    function domain() external view returns (uint32);

    /// @notice Returns protocol gas fee on harvest, in `BPS`.
    function protocolCompoundFee() external view returns (uint256);

    /// @notice Returns protocol yield fee on strategy harvest, in `BPS`.
    function protocolYieldFee() external view returns (uint256);

    /// @notice Returns protocol yield + gas fee on strategy harvest,
    ///         in `BPS`.
    function protocolHarvestFee() external view returns (uint256);

    /// @notice Returns protocol fee on leverage actions, in `BPS`.
    function protocolLeverageFee() external view returns (uint256);

    /// @notice Returns default fee on interest generated from active loans,
    ///         in `BPS`.
    function defaultProtocolInterestFee() external view returns (uint256);

    /// @notice Returns earlyUnlockPenaltyMultiplier value, in `BPS`.
    function earlyUnlockPenaltyMultiplier() external view returns (uint256);

    /// @notice Returns voteBoostMultiplier value, in `BPS`.
    function voteBoostMultiplier() external view returns (uint256);

    /// @notice Returns lockBoostMultiplier value, in `BPS`.
    function lockBoostMultiplier() external view returns (uint256);

    /// @notice Returns swap slippage limit, in `BPS`.
    function slippageLimit() external view returns (uint256);

    /// @notice Returns an array of Chain IDs recorded in the Crosschain
    ///         Protocol's Chain ID format.
    function foreignChainIds() external view returns (uint256[] memory);

    /// @notice Returns an array of Curvance markets on this chain.
    function marketManagers() external view returns (address[] memory);

    /// @notice Increments a caller's approval index.
    /// @dev By incrementing their approval index, a user's delegates will all
    ///      have their delegation authority revoked across all Curvance
    ///      contracts.
    ///      Emits an {ApprovalIndexIncremented} event.
    function incrementApprovalIndex() external;

    /// @notice Returns `user`'s approval index.
    /// @param user The user to check approval index for.
    function userApprovalIndex(address user) external view returns (uint256);

    /// @notice Returns whether a user has delegation disabled.
    /// @param user The user to check delegation status for.
    function checkNewDelegationDisabled(
        address user
    ) external view returns (bool);

    /// @notice Returns whether a particular GETH chainId is supported.
    /// ChainId => messagingHub address, 2 = supported; 1 = unsupported.
    function chainConfig(
        uint256 chainId
    ) external view returns (ChainConfig memory);

    /// @notice Returns the GETH chainId corresponding chainId corresponding
    ///         to Crosschain Messaging Protocol's `chainId`.
    /// @param chainId The Crosschain Messaging Protocol's chainId.
    /// @return The GETH chainId corresponding chainId corresponding to
    ///         Crosschain Messaging Protocol's `chainId`.
    function messagingToGETHChainId(
        uint16 chainId
    ) external view returns (uint256);

    /// @notice Returns the Crosschain Messaging Protocol's ChainId
    ///         corresponding to the GETH `chainId`.
    /// @param chainId The GETH chainId.
    /// @return The Crosschain Messaging Protocol's ChainId
    ///         corresponding to the GETH `chainId`.
    function GETHToMessagingChainId(
        uint256 chainId
    ) external view returns (uint256);

    /// @notice Indicates if an address is a market manager or not.
    function isMarketManager(
        address addressToCheck
    ) external view returns (bool);

    /// @notice Maps an intent target address to the contract that will
    ///         inspect provided external calldata.
    function externalCalldataChecker(
        address addressToCheck
    ) external view returns (address);

    /// @notice Maps a Multicall target address to the contract that will
    ///         inspect provided multicall calldata.
    function multicallChecker(
        address addressToCheck
    ) external view returns (address);

    /// @notice Indicates the amount of token rewards allocated on this chain,
    ///         for an epoch.
    function emissionsAllocatedByEpoch(
        uint256 epoch
    ) external view returns (uint256);

    /// @notice Indicates the amount of token rewards allocated across all
    ///         chains, for an era. An era is a particular period in time in
    ///         which rewards are constant, before a halvening event moves the
    ///         protocol to a new era.
    function targetEmissionAllocationByEra(
        uint256 era
    ) external view returns (uint256);

    /// @notice Unlocks a market to process auction-based liquidations.
    /// @param marketToUnlock The address of the market manager to unlock
    ///                       auction-based liquidations with a specific
    ///                       liquidation bonus.
    function unlockAuctionForMarket(address marketToUnlock) external;

    /// @notice Checks if a market is unlocked for auction operations.
    /// @return Whether the caller is an unlocked market, approved for
    ///         auction-based liquidations.
    function isMarketUnlocked() external view returns (bool);

    /// @notice Sets the amount of token rewards allocated on this chain,
    ///         for an epoch.
    /// @dev Only callable by the Voting Hub.
    /// @param epoch The epoch having its token emission values set.
    /// @param emissionsAllocated The amount of token rewards allocated on
    ///                           this chain, for an epoch.
    function setEmissionsAllocatedByEpoch(
        uint256 epoch,
        uint256 emissionsAllocated
    ) external;

    /// @notice Checks whether `user` has transferability enabled or disabled
    ///         for their tokens.
    /// @dev This is inherited from ActionRegistry portion of centralRegistry.
    /// @param user The address to check whether transferability is enabled or
    ///             disabled for.
    /// @return result Indicates whether `user` has transferability disabled
    ///                or not, true = disabled, false = not disabled.
    function checkTransfersDisabled(
        address user
    ) external view returns (bool result);
}

// contracts/interfaces/IDynamicIRM.sol

interface IDynamicIRM {
    /// @notice Returns the interval at which interest rates are adjusted.
    /// @notice The interval at which interest rates are adjusted,
    ///         in seconds.
    function ADJUSTMENT_RATE() external view returns (uint256);

    /// @notice The borrowable token linked to this interest rate model
    ///         contract.
    function linkedToken() external view returns (address);

    /// @notice Calculates the current borrow rate, per second.
    /// @dev This function's intention is for frontend data querying and
    ///     should not be used for onchain execution.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return result The borrow interest rate percentage, per second,
    ///                in `WAD`.
    function borrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) external view returns (uint256 result);

    /// @notice Calculates the current borrow rate per second,
    ///         with updated vertex multiplier applied.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return result The borrow rate percentage per second, in `WAD`.
    function predictedBorrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) external view returns (uint256 result);

    /// @notice Calculates the current supply rate, per second.
    /// @dev This function's intention is for frontend data querying and
    ///     should not be used for onchain execution.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @param interestFee The current interest rate protocol fee
    ///                    for the market token.
    /// @return result The supply interest rate percentage, per second,
    ///                in `WAD`.
    function supplyRate(
        uint256 assetsHeld,
        uint256 debt,
        uint256 interestFee
    ) external view returns (uint256 result);

    /// @notice Calculates the interest rate paid per second by borrowers,
    ///         in percentage paid, per second, in `WAD`, and updates
    ///         `vertexMultiplier` if necessary.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return ratePerSecond The interest rate paid per second by borrowers,
    ///                       in percentage paid, per second, in `WAD`.
    /// @return adjustmentRate The period of time at which interest rates are
    ///                        adjusted, in seconds.
    function adjustedBorrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) external returns (uint256 ratePerSecond, uint256 adjustmentRate);

    /// @notice Calculates the borrow utilization rate of the market.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param outstandingDebt The amount of outstanding debt in the pool.
    /// @return The utilization rate between [0, WAD].
    function utilizationRate(
        uint256 assetsHeld,
        uint256 outstandingDebt
    ) external view returns (uint256);
}

// contracts/interfaces/IERC165.sol

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * 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[EIP 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);
}

// contracts/interfaces/IERC20.sol

// @dev Interface of the ERC20 standard
interface IERC20 {
    // @dev Returns the name of the token.
    function name() external view returns (string memory);

    // @dev Returns the symbol of the token.
    function symbol() external view returns (string memory);

    // @dev Returns the decimals of the token.
    function decimals() external view returns (uint8);

    // @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 amount of tokens in existence.
    function totalSupply() external view returns (uint256);

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

    // @dev Moves `amount` 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 amount) external returns (bool);

    // @dev Moves `amount` tokens from `from` to `to` using the
    //      allowance mechanism. `amount` 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 amount
    ) 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.
    function allowance(
        address owner,
        address spender
    ) external view returns (uint256);

    //  @dev Sets `amount` 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 amount) external returns (bool);
}

// contracts/interfaces/IMarketManager.sol

interface IMarketManager {
    /// TYPES ///

    /// @notice Data structure passed communicating the intended liquidation
    ///         scenario to review based on current liquidity levels.
    /// @param collateralToken The token which was used as collateral
    ///                        by `account` and may be seized.
    /// @param debtToken The token to potentially repay which has
    ///                  outstanding debt by `account`.
    /// @param numAccounts The number of accounts to be, potentially,
    ///                    liquidated.
    /// @param liquidateExact Whether the liquidator desires a specific
    ///                       liquidation amount.
    /// @param liquidatedShares Empty variable slot to store how much
    ///                         `collateralToken` will be seized as
    ///                         part of a particular liquidation.
    /// @param debtRepaid Empty variable slot to store how much
    ///                   `debtToken` will be repaid as part of a
    ///                   particular liquidation.
    /// @param badDebt Empty variable slot to store how much bad debt will
    ///                be realized by lenders as part of a particular
    ///                liquidation.
    struct LiqAction {
        address collateralToken;
        address debtToken;
        uint256 numAccounts;
        bool liquidateExact;
        uint256 liquidatedShares;
        uint256 debtRepaid;
        uint256 badDebt;
    }

    /// @notice Data structure returned communicating outcome of a liquidity
    ///         scenario review based on current liquidity levels.
    /// @param liquidatedShares An array containing the collateral amounts to
    ///                         liquidate from accounts, in shares.
    /// @param debtRepaid The total amount of debt to repay from accounts,
    ///                   in assets.
    /// @param badDebtRealized The total amount of debt to realize as losses
    ///                        for lenders, in assets.
    struct LiqResult {
        uint256[] liquidatedShares;
        uint256 debtRepaid;
        uint256 badDebtRealized;
    }

    /// @notice Returns whether minting, collateralization, borrowing of
    ///         `cToken` is paused.
    /// @param cToken The address of the Curvance token to return
    ///               action statuses of.
    /// @return bool Whether minting `cToken` is paused or not.
    /// @return bool Whether collateralization `cToken` is paused or not.
    /// @return bool Whether borrowing `cToken` is paused or not.
    function actionsPaused(
        address cToken
    ) external view returns (bool, bool, bool);

    /// @notice Returns the current collateralization configuration
    ///         of `cToken`.
    /// @param cToken The address of the Curvance token to return
    ///               collateralization configuration of.
    /// @return The ratio at which this token can be borrowed against
    ///         when collateralized.
    /// @return The collateral requirement where dipping below this
    ///         will cause a soft liquidation.
    /// @return The collateral requirement where dipping below
    ///         this will cause a hard liquidation.
    function collConfig(address cToken) external view returns (
         uint256, uint256, uint256
    );

    /// @notice Returns the current liquidation configuration
    ///         of `cToken`.
    /// @param cToken The address of the Curvance token to return
    ///               liquidation configuration of.
    /// @return The base ratio at which this token will be
    ///         compensated on soft liquidation.
    /// @return The liquidation incentive curve length between soft
    ///         liquidation to hard liquidation, in `WAD`. e.g. 5% base
    ///         incentive with 8% curve length results in 13% liquidation
    ///         incentive on hard liquidation.
    /// @return The minimum possible liquidation incentive for during an
    ///         auction, in `WAD`.
    /// @return The maximum possible liquidation incentive for during an
    ///         auction, in `WAD`.
    /// @return Maximum % that a liquidator can repay when soft
    ///         liquidating an account, in `WAD`.
    /// @return Curve length between soft liquidation and hard liquidation,
    ///         should be equal to 100% - `closeFactorBase`, in `WAD`.
    /// @return The minimum possible close factor for during an auction,
    ///         in `WAD`.
    /// @return The maximum possible close factor for during an auction,
    ///         in `WAD`.
    function liquidationConfig(address cToken) external view returns (
        uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256
    );

    /// @notice Enables an auction-based liquidation, potentially with a dynamic
    ///         close factor and liquidation penalty values in transient storage.
    /// @dev Transient storage enforces any liquidator outside auction-based
    ///      liquidations uses the default risk parameters.
    /// @param cToken The Curvance token to configure liquidations for during
    ///               an auction-based liquidation.
    /// @param incentive The auction liquidation incentive value, in `BPS`.
    /// @param closeFactor The auction close factor value, in `BPS`.
    function setTransientLiquidationConfig(
        address cToken,
        uint256 incentive,
        uint256 closeFactor
    ) external;

    /// @notice Called from the AuctionManager as a post hook after liquidations
    ///         are tried to enable all collateral to be liquidated outside
    ///         an Auction tx.
    /// @notice Resets the liquidation risk parameters in transient storage to
    ///         zero.
    /// @dev This is redundant since the transient values will be reset after
    ///      the liquidation transaction, but can be useful during meta calls
    ///      with multiple liquidations during a single transaction. 
    function resetTransientLiquidationConfig() external;

    /// @notice Checks if the account should be allowed to mint tokens
    ///         in the given market.
    /// @param cToken The token to verify mints against.
    function canMint(address cToken) external;

    /// @notice Checks if the account should be allowed to collateralize
    ///         their shares of the given market.
    ///         Prunes unused positions in `account` data.
    /// @dev May emit a {PositionUpdated} event.
    /// @param cToken The token to verify collateralization of.
    /// @param account The account which would collateralize the asset.
    /// @param newNetCollateral The amount of shares that would be
    ///                         collateralized in total if allowed.
    function canCollateralize(
        address cToken,
        address account,
        uint256 newNetCollateral
    ) external;

    /// @notice Checks if the account should be allowed to redeem tokens
    ///         in the given market, and then redeems.
    /// @dev This can only be called by the cToken itself.
    /// @param cToken The token to verify the redemption against.
    /// @param shares The number of cToken shares to redeem for the
    ///               underlying asset in the market.
    /// @param account The account which would redeem `shares`.
    /// @param balanceOf The current cToken share balance of `account`.
    /// @param collateralPosted The current cToken shares posted as
    ///                         collateral by `account`.
    /// @param forceRedeemCollateral Whether the collateral should be always
    ///                              reduced.
    function canRedeemWithCollateralRemoval(
        address cToken,
        uint256 shares,
        address account,
        uint256 balanceOf,
        uint256 collateralPosted,
        bool forceRedeemCollateral
    ) external returns (uint256);

    /// @notice Checks if the account should be allowed to borrow
    ///         the underlying asset of the given market.
    ///         Prunes unused positions in `account` data.
    /// @dev May emit a {PositionUpdated} event.
    /// @param cToken The token to verify borrowability of.
    /// @param assets The amount of underlying assets `account` would borrow.
    /// @param account The account which would borrow the asset.
    /// @param newNetDebt The amount of assets that would be
    ///                   outstanding debt in total if allowed.
    function canBorrow(
        address cToken,
        uint256 assets,
        address account,
        uint256 newNetDebt
    ) external;

    /// @notice Checks if the account should be allowed to borrow
    ///         the underlying asset of the given market,
    ///         and notifies the market of the borrow.
    /// @dev This can only be called by the market itself.
    /// @param cToken The market to verify the borrow against.
    /// @param assets The amount of underlying assets `account` would borrow.
    /// @param account The account which would borrow the asset.
    /// @param newNetDebt The amount of assets that would be
    ///                   outstanding debt in total if allowed.
    function canBorrowWithNotify(
        address cToken,
        uint256 assets,
        address account,
        uint256 newNetDebt
    ) external;

    /// @notice Checks if the account should be allowed to repay a borrow
    ///         in the given market, may clean up positions.
    /// @param cToken The Curvance token to verify the repayment of.
    /// @param newNetDebt The new debt amount owed by `account` after
    ///                   repayment.
    /// @param debtAsset The debt asset being repaid to `cToken`.
    /// @param decimals The decimals that `debtToken` is measured in.
    /// @param account The account who will have their loan repaid.
    function canRepayWithReview(
        address cToken,
        uint256 newNetDebt,
        address debtAsset,
        uint256 decimals,
        address account
    ) external;

    /// @notice Checks if the liquidation should be allowed to occur,
    ///         and returns how many collateralized shares should be seized
    ///         on liquidation.
    /// @param debtAmounts The amounts of outstanding debt the liquidator
    ///                    wishes to repay, in underlying assets, empty if
    ///                    intention is to liquidate maximum amount possible
    ///                    for each account.
    /// @param liquidator The address of the account trying to liquidate
    ///                   `accounts`.
    /// @param accounts The addresses of the accounts to be liquidated.
    /// @param action A LiqAction struct containing:
    ///               collateralToken The token which is used as collateral
    ///                               by `account` and may be seized.
    ///               debtToken The token to potentially repay which has
    ///                         outstanding debt by `account`.
    ///               numAccounts The number of accounts to be, potentially,
    ///                           liquidated.
    ///               liquidateExact Whether the liquidator desires a
    ///                              specific liquidation amount.
    ///               collateralLiquidated Empty variable slot to store how
    ///                                    much `collateralToken` will be
    ///                                    seized as part of a particular
    ///                                    liquidation.
    ///               debtRepaid Empty variable slot to store how much
    ///                          `debtToken` will be repaid as part of a
    ///                          particular liquidation.
    ///               badDebt Empty variable slot to store how much bad debt
    ///                       will be realized as part of a particular
    ///                       liquidation.
    /// @return result A LiqResult struct containing:
    ///                liquidatedShares An array containing the collateral
    ///                                 amounts to liquidate from
    ///                                 `accounts`.
    ///                debtRepaid The total amount of debt to repay from
    ///                           `accounts`.
    ///                badDebtRealized The total amount of debt to realize as
    ///                                losses for lenders inside this market.
    /// @return An array containing the debt amounts to repay from
    ///        `accounts`, in assets.
    function canLiquidate(
        uint256[] memory debtAmounts,
        address liquidator,
        address[] calldata accounts,
        IMarketManager.LiqAction memory action
    ) external returns (LiqResult memory, uint256[] memory);

    /// @notice Checks if the seizing of `collateralToken` by repayment of
    ///         `debtToken` should be allowed.
    /// @param collateralToken The Curvance token which was used as collateral
    ///                        and will be seized.
    /// @param debtToken The Curvance token which has outstanding debt to and
    ///                  would be repaid during `collateralToken` seizure.
    function canSeize(address collateralToken, address debtToken) external;

    /// @notice Checks if the account should be allowed to transfer collateral
    ///         tokens in the given market.
    /// @param cToken The Curvance token to verify the transfer of.
    /// @param shares The amount of `cToken` to transfer.
    /// @param account The account which will transfer `shares`.
    /// @param balanceOf The current balance that `account` has of `cToken`
    ///                  shares.
    /// @param collateralPosted The amount of `cToken` shares posted as
    ///                         collateral by `account`.
    /// @param isCollateral Boolean indicating whether the token is currently
    ///                     being used as collateral.
    function canTransfer(
        address cToken,
        uint256 shares,
        address account,
        uint256 balanceOf,
        uint256 collateralPosted,
        bool isCollateral
    ) external returns (uint256);

    /// @notice Updates `account` cooldownTimestamp to the current block
    ///         timestamp.
    /// @dev The caller must be a listed cToken in the `markets` mapping.
    /// @param cToken The address of the token that the account is borrowing.
    /// @param account The address of the account that has just borrowed.
    function notifyBorrow(address cToken, address account) external;

    /// @notice A list of all tokens inside this market for
    ///         offchain querying.
    function queryTokensListed() external view returns (address[] memory);

    /// @notice Returns whether `cToken` is listed in the lending market.
    /// @param cToken market token address.
    function isListed(address cToken) external view returns (bool);

    /// @notice The total amount of `cToken` that can be posted as collateral,
    ///         in shares.
    function collateralCaps(address cToken) external view returns (uint256);

    /// @notice The total amount of `cToken` underlying that can be borrowed,
    ///         in assets.
    function debtCaps(address cToken) external view returns (uint256);

    /// @notice Returns whether `addressToCheck` is an approved position
    ///         manager or not.
    /// @param addressToCheck Address to check for position management
    ///                       authority.
    function isPositionManager(
        address addressToCheck
    ) external view returns (bool);

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

    /// @notice Determine `account`'s current status between collateral,
    ///         debt, and additional liquidity.
    /// @param account The account to determine liquidity for.
    /// @return The current total collateral amount of `account`.
    /// @return The maximum debt amount of `account` can take out with
    ///         their current collateral.
    /// @return The current total borrow amount of `account`.
    function statusOf(
        address account
    ) external returns (uint256, uint256, uint256);
}

// contracts/interfaces/IMulticallChecker.sol

interface IMulticallChecker {
    function checkCalldata(
        address caller,
        address target,
        bytes memory data
    ) external;
}

// contracts/libraries/LowLevelCallsHelper.sol

library LowLevelCallsHelper {
    /// ERRORS ///

    error LowLevelCallsHelper__InsufficientBalance();
    error LowLevelCallsHelper__CallFailed();

    /// INTERNAL FUNCTIONS ///

    /// @notice Executes a low level .call() and validates that execution
    ///         was safely performed.
    /// @dev Bubbles up errors and reverts if anything along the execution
    ///      path failed or was a red flag.
    /// @param target The target contract address to execute .call() at.
    /// @param data The bytecode data to attach to the .call() execution
    ///             including the function signature hash and function call
    ///             parameters.
    function _call(
        address target,
        bytes memory data
    ) internal returns (bytes memory) {
        (bool success, bytes memory returnData) = target.call{value: 0}(data);
        
        return _verifyResult(target, success, returnData);
    }

    /// @notice Executes a low level .call(), with attached native token
    ///         and validates that execution was safely performed.
    /// @dev Bubbles up errors and reverts if anything along the execution
    ///      path failed or was a red flag.
    /// @param target The target contract address to execute .call() at.
    /// @param data The bytecode data to attach to the .call() execution
    ///             including the function signature hash and function call
    ///             parameters.
    /// @param value The amount of native token to attach to the low level
    ///              call.
    function _callWithNative(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert LowLevelCallsHelper__InsufficientBalance();
        }

        (bool success, bytes memory returnData) = target.call{
            value: value
        }(data);

        return _verifyResult(target, success, returnData);
    }

    /// @notice Executes a low level .delegatecall() and validates that
    ///         execution was safely performed.
    /// @dev Bubbles up errors and reverts if anything along the execution
    ///      path failed or was a red flag.
    /// @param target The target contract address to execute .delegatecall()
    ///               at.
    /// @param data The bytecode data to attach to the .call() execution
    ///             including the function signature hash and function call
    ///             parameters.
    function _delegateCall(
        address target,
        bytes memory data
    ) internal returns (bytes memory) {
        (bool success, bytes memory returnData) = target.delegatecall(data);

        return _verifyResult(target, success, returnData);
    }

    /// @dev Validates whether the low level call or delegate call was
    ///      successful and reverts in cases of bubbled up revert messages
    ///      or if `target` was not actually a contract.
    function _verifyResult(
        address target,
        bool success,
        bytes memory returnData
    ) internal view returns (bytes memory) {
        _propagateError(success, returnData);

        // If the call was successful but there was no return data we need
        // to make sure a contract was actually called as expected.
        if (returnData.length == 0 && target.code.length == 0) {
            revert LowLevelCallsHelper__CallFailed();
        }

        return returnData;
    }

    /// @dev Propagates an error message, if necessary.
    /// @param success If transaction was successful.
    /// @param resultData The transaction result data.
    function _propagateError(
        bool success,
        bytes memory resultData
    ) internal pure {
        if (!success) {
            if (resultData.length == 0) {
                revert LowLevelCallsHelper__CallFailed();
            }

            // Bubble up error if there was one given.
            assembly {
                revert(add(32, resultData), mload(resultData))
            }
        }
    }
}

// contracts/libraries/external/SafeTransferLib.sol

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

// contracts/libraries/external/ERC165.sol

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(
        bytes4 interfaceId
    ) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// contracts/libraries/external/ERC165Checker.sol

// @author Modified from OpenZeppelin Contracts (utils/introspection/ERC165Checker.sol)

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(
                account,
                type(IERC165).interfaceId
            ) &&
            !supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(
        address account,
        bytes4 interfaceId
    ) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return
            supportsERC165(account) &&
            supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(
        address account,
        bytes4 interfaceId
    ) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeCall(
            IERC165.supportsInterface,
            (interfaceId)
        );

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(
                30000,
                account,
                add(encodedParams, 0x20),
                mload(encodedParams),
                0x00,
                0x20
            )
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

// contracts/libraries/CentralRegistryLib.sol

/// @title Curvance Central Registry Validation Library
/// @notice For validating that Central Registry is configured properly
///         on launch.
library CentralRegistryLib {
    /// ERRORS ///

    error CentralRegistryLib__InvalidCentralRegistry();

    /// INTERNAL FUNCTIONS ///

    /// @notice Validates that `cr` is the Protocol Central Registry.
    /// @param cr The proposed Protocol Central Registry.
    function _isCentralRegistry(ICentralRegistry cr) internal view {
        if (
            !ERC165Checker.supportsInterface(
                address(cr),
                type(ICentralRegistry).interfaceId
            )
        ) {
            revert CentralRegistryLib__InvalidCentralRegistry();
        }
    }
}

// contracts/libraries/Multicall.sol

/// @title Curvance Multicall helper.
/// @notice Multicall implementation to support pull based oracles and
///         other chained actions within Curvance.
abstract contract Multicall {
    /// TYPES ///

    /// @notice Struct containing information on the desired
    ///         multicall action to execute. 
    /// @param target The address of the target contract to execute the call at.
    /// @param isPriceUpdate Boolean indicating if the call is a price update.
    /// @param data The data to attach to the call.
    struct MulticallAction {
        address target;
        bool isPriceUpdate;
        bytes data;
    }

    /// ERRORS ///

    error Multicall__InvalidTarget();
    error Multicall__UnknownCalldata();

    /// EXTERNAL FUNCTIONS ///

    /// @notice Executes multiple calls in a single transaction.
    ///         This can be used to update oracle prices before
    ///         a liquidity dependent action.
    function multicall(
        MulticallAction[] calldata calls
    ) external returns (bytes[] memory results) {
        ICentralRegistry cr = _getCentralRegistry();
        uint256 numCalls = calls.length;
        results = new bytes[](numCalls);
        MulticallAction memory cachedCall;

        for (uint256 i; i < numCalls; ++i) {
            cachedCall = calls[i];
            
            if (cachedCall.isPriceUpdate) {
                // CASE: We need to update a pull based price oracle and we
                //       need a direct call to the target address.
                address checker = cr.multicallChecker(cachedCall.target);

                // Validate we know how to verify this calldata.
                if (checker == address(0)) {
                    revert Multicall__UnknownCalldata();
                }

                IMulticallChecker(checker).checkCalldata(
                    msg.sender,
                    cachedCall.target,
                    cachedCall.data
                );

                results[i] = LowLevelCallsHelper.
                    _call(cachedCall.target, cachedCall.data);
                
                continue;
            }

            // CASE: Not a price update and we need delegate the call to the
            //       current address.

            if (address(this) != cachedCall.target) {
                revert Multicall__InvalidTarget();
            }

            results[i] = LowLevelCallsHelper.
                _delegateCall(address(this), cachedCall.data);
        }
    }

    /// @notice Returns the Protocol Central Registry contract in interface
    ///         form.
    /// @dev MUST be overridden in every multicallable contract's
    ///      implementation.
    function _getCentralRegistry()
        internal
        view
        virtual
        returns (ICentralRegistry);
}

// contracts/libraries/CommonLib.sol

/// @title Curvance Common Library
/// @notice A utility library for common functions used throughout the
///        Curvance Protocol.
library CommonLib {

    /// @notice Returns whether `tokenA` matches `tokenB` or not.
    /// @param tokenA The first token address to compare.
    /// @param tokenB The second token address to compare.
    /// @return Whether `tokenA` matches `tokenB` or not.
    function _isMatchingToken(
        address tokenA,
        address tokenB
    ) internal pure returns (bool) {
        if (tokenA == tokenB) {
            return true;
        }

        if (_isNative(tokenA) && _isNative(tokenB)) {
            return true;
        }

        return false;
    }

    /// @notice Returns whether `token` is referring to network gas token
    ///         or not.
    /// @param token The address to review.
    /// @return result Whether `token` is referring to network gas token or
    ///                not.
    function _isNative(address token) internal pure returns (bool result) {
        result = token == address(0) ||
            token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    }

    /// @notice Returns balance of `token` for this contract.
    /// @param token The token address to query balance of.
    /// @return b The balance of `token` inside address(this).
    function _balanceOf(address token) internal view returns (uint256 b) {
        b = _isNative(token) ? address(this).balance :
            IERC20(token).balanceOf(address(this));
    }

    /// @notice Returns the Oracle Manager in interface form from `cr`.
    /// @param cr The address of the Protocol Central Registry.
    /// @return oracleManager The Oracle Manager in interface form.
    function _oracleManager(
        ICentralRegistry cr
    ) internal view returns (IOracleManager oracleManager) {
        oracleManager = IOracleManager(cr.oracleManager());
    }
}

// contracts/interfaces/IBorrowableCToken.sol

interface IBorrowableCToken {
    /// ICToken GENERIC FUNCTIONS ///

    /// @notice Starts a cToken market, executed via marketManager.
    /// @dev This initial mint is a failsafe against rounding exploits,
    ///      although, we protect against them in many ways,
    ///      better safe than sorry.
    /// @param by The account initializing deposits.
    function initializeDeposits(address by) external returns (bool);

    /// @notice Returns the decimals of the cToken.
    /// @dev We pull directly from underlying incase its a proxy contract,
    ///      and changes decimals on us.
    /// @return The number of decimals for this cToken,
    ///         matching the underlying token.
    function decimals() external view returns (uint8);

    /// @notice Returns whether the underlying token can be borrowed.
    /// @dev true = Borrowable; false = Not Borrowable.
    /// @return Whether this token is borrowable or not.
    function isBorrowable() external view returns (bool);

    /// @notice The token balance of an account.
    /// @dev Account address => account token balance.
    /// @param account The address of the account to query token balance for.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the address of the underlying asset.
    /// @return The address of the underlying asset.
    function asset() external view returns (address);

    /// @notice Returns the current vesting yield information.
    /// @return vestingRate % per second in `asset()`.
    /// @return vestingEnd When the current vesting period ends and interest
    ///                    rates paid will update.
    /// @return lastVestingClaim Last time pending vested yield was claimed.
    /// @return debtIndex The current market debt index.
    function getYieldInformation() external view returns (
        uint256 vestingRate,
        uint256 vestingEnd,
        uint256 lastVestingClaim,
        uint256 debtIndex
    );

    /// @notice Get a snapshot of `account` data in this Curvance token.
    /// @dev Used by marketManager to more efficiently perform
    ///      liquidity checks.
    ///      NOTE: Does not accrue pending interest as part of the call.
    /// @param account The address of the account to snapshot.
    /// @return The account snapshot of `account`.
    function getSnapshot(
        address account
    ) external view returns (AccountSnapshot memory);

    /// @notice Total number of cTokens in circulation.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the total amount of assets held by the market.
    /// @return The total amount of assets held by the market.
    function totalAssets() external view returns (uint256);

    /// @notice Address of the Market Manager linked to this contract.
    function marketManager() external view returns (IMarketManager);

    /// @notice Returns the amount of assets that would be exchanged
    ///         by the vault for `shares` provided.
    /// @param shares The number of shares to theoretically use
    ///               for conversion to assets.
    /// @return The number of assets a user would receive for converting
    ///         `shares`.
    function convertToAssets(uint256 shares) external view returns (uint256);

    /// @notice Returns share -> asset exchange rate, in `WAD`.
    /// @dev Oracle Manager calculates cToken value from this exchange rate.
    function exchangeRate() external view returns (uint256);

    /// @notice Executes multiple calls in a single transaction.
    ///         This can be used to update oracle prices before
    ///         a liquidity dependent action.
    function multicall(
        Multicall.MulticallAction[] memory calls
    ) external returns (bytes[] memory results);

    /// @notice Caller deposits `assets` into the market and `receiver`
    ///         receives shares.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function deposit(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Caller deposits `assets` into the market, `receiver` receives
    ///         shares, and collateralization of `assets` is enabled.
    /// @dev The caller must be depositing for themselves, or be managing
    ///      their position through a Position Manager contract.
    ///      If the caller is not approved to collateralize the function will
    ///      simply deposit assets on behalf of `receiver`.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function depositAsCollateral(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Caller deposits `assets` into the market, `receiver` receives
    ///         shares, and collateralization of `assets` is enabled.
    /// @dev Requires that `receiver` approves the caller prior to
    ///      collateralize on their behalf.
    ///      NOTE: Be careful who you approve here!
    ///      They can delay redemption of assets through repeated
    ///      collateralization preventing withdrawal.
    ///      If the caller is not approved to collateralize the function will
    ///      simply deposit assets on behalf of `receiver`.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function depositAsCollateralFor(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Withdraws assets, quoted in `shares` from the market,
    ///         and burns `owner` shares.
    /// @dev Does not force collateral to be withdrawn.
    /// @param shares The amount of shares to be redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner`.
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Withdraws assets, quoted in `shares` from the market,
    ///         and burns `owner` shares, sending assets to `receiver`.
    /// @dev Does not force collateral to be withdrawn.
    /// @param shares The amount of shares to redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner` and sent to
    ///                `receiver`.
    function redeemFor(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Caller withdraws assets from the market and burns their
    ///         shares, on behalf of `owner`.
    /// @dev Forces collateral to be withdrawn from `owner` collateralPosted.
    /// @param shares The amount of shares to redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner` and sent to
    ///                `receiver`.
    function redeemCollateralFor(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Used by a Position Manager contract to redeem assets from
    ///         collateralized shares by `account` to perform a complex
    ///         action.
    /// @param assets The amount of the underlying assets to redeem.
    /// @param owner The owner address of assets to redeem.
    /// @param action Instructions for a deleverage action containing:
    ///               cToken Address of the cToken that will be redeemed from
    ///                      and assets swapped into `borrowableCToken` asset.
    ///               collateralAssets The amount of `cToken` that will be
    ///                                deleveraged, in assets.
    ///               borrowableCToken Address of the borrowableCToken that
    ///                                will have its debt paid.
    ///               repayAssets The amount of `borrowableCToken` asset that
    ///                           will be repaid to lenders.
    ///               swapActions Swap actions instructions converting
    ///                           collateral asset into debt asset to
    ///                           facilitate deleveraging.
    ///               auxData Optional auxiliary data for execution of a
    ///                       deleverage action.
    function withdrawByPositionManager(
        uint256 assets,
        address owner,
        IPositionManager.DeleverageAction memory action
    ) external;

    /// @notice Amount of tokens that has been posted as collateral,
    ///         in shares.
    function marketCollateralPosted() external view returns (uint256);

    /// @notice Shares of this token that an account has posted as collateral.
    /// @param account The address of the account to check collateral posted
    ///                of.
    function collateralPosted(address account) external view returns (uint256);

    /// @notice Transfers tokens from `account` to `liquidator`.
    /// @dev Will fail unless called by a cToken during the process
    ///      of liquidation.
    /// @param shares An array containing the number of cToken shares
    ///               to seize.
    /// @param liquidator The account receiving seized cTokens.
    /// @param accounts An array containing the accounts having
    ///                 collateral seized.
    function seize(
        uint256[] calldata shares,
        address liquidator,
        address[] calldata accounts
    ) external;

    /// @notice Allows users to simulate the effects of their deposit at
    ///         the current block.
    /// @param assets The number of assets to preview a deposit call.
    /// @return The shares received for depositing `assets`.
    function previewDeposit(uint256 assets) external view returns (uint256);

    /// IBorrowableCToken SPECIFIC FUNCTIONS ///

    /// @notice Address of the current Dynamic IRM.
    function IRM() external view returns (IDynamicIRM);

    /// @notice Fee that goes to protocol for interested generated for
    ///         lenders, in `WAD`.
    function interestFee() external view returns (uint256);

    /// @notice Can accrue interest yield, configure next interest accrual
    ///         period, and updates vesting data, if needed.
    /// @dev May emit a {RatesAdjusted} event.
    function accrueIfNeeded() external;

    /// @notice The amount of tokens that has been borrowed as debt,
    ///         in assets.
    function marketOutstandingDebt() external view returns (uint256);

    /// @notice Returns the current debt balance for `account`.
    /// @dev Note: Pending interest is not applied in this calculation.
    /// @param account The address whose debt balance should be calculated.
    /// @return result The current outstanding debt balance of `account`.
    function debtBalance(
        address account
    ) external view returns (uint256 result);

    /// @notice Updates pending interest and returns the current outstanding
    ///         debt owed by `account`.
    /// @param account The address whose debt balance should be calculated.
    /// @return result The current outstanding debt of `account`, with pending
    ///                interest applied.
    function debtBalanceUpdated(
        address account
    ) external returns (uint256 result);

    /// @notice Updates pending interest and returns the up-to-date exchange
    ///         rate from the underlying to the BorrowableCToken.
    /// @return result The share -> asset exchange rate, in `WAD`.
    function exchangeRateUpdated() external returns (uint256);

    /// @notice Used by a delegated user to borrow underlying tokens
    ///         from lenders, based on collateral posted inside this market
    ///         by `account`.
    /// @dev Updates pending interest before executing the borrow.
    ///      NOTE: Be careful who you approve here!
    ///      Not only can they take borrowed funds, but, they can delay
    ///      repayment through repeated borrows preventing withdrawal.
    /// @param assets The amount of the underlying asset to borrow.
    /// @param receiver The account who will receive the borrowed assets.
    /// @param owner The account who will have their assets borrowed
    ///              against.
    function borrowFor(
        uint256 assets,
        address receiver,
        address owner
    ) external;

    /// @notice Used by a Position Manager contract to borrow assets from
    ///         lenders, based on collateralized shares by `account` to
    ///         perform a complex action.
    /// @param assets The amount of the underlying assets to borrow.
    /// @param owner The account address to borrow on behalf of.
    /// @param action Instructions for a leverage action containing:
    ///               borrowableCToken Address of the borrowableCToken that
    ///                                will be borrowed from and assets
    ///                                swapped into `cToken` asset.
    ///               borrowAssets The amount borrowed from
    ///                            `borrowableCToken`, in assets.
    ///               cToken Curvance token assets that borrowed funds will be
    ///                      swapped into.
    ///               swapAction Swap action instructions converting debt
    ///                          asset into collateral asset to facilitate
    ///                          leveraging.
    ///               auxData Optional auxiliary data for execution of a
    ///                       leverage action.
    function borrowForPositionManager(
        uint256 assets,
        address owner,
        IPositionManager.LeverageAction memory action
    ) external;

    /// @notice Repays underlying tokens to lenders, on behalf of `account`,
    ///         freeing up their collateral posted inside this market.
    /// @dev Updates pending interest before executing the repay.
    /// @param assets The amount to repay, or 0 for the full outstanding
    ///               amount.
    /// @param owner The account address to repay on behalf of.
    function repayFor(uint256 assets, address owner) external;

    /// @notice Gets balance of this contract, in terms of the underlying.
    /// @dev This excludes changes in underlying token balance by the
    ///      current transaction, if any.
    /// @return The quantity of underlying tokens held by the market.
    function assetsHeld() external view returns (uint256);
}

// contracts/interfaces/ICToken.sol

struct AccountSnapshot {
    address asset;
    address underlying;
    uint8 decimals;
    bool isCollateral;
    uint256 collateralPosted;
    uint256 debtBalance;
}

interface ICToken {
    /// @notice Starts a cToken market, executed via marketManager.
    /// @dev This initial mint is a failsafe against rounding exploits,
    ///      although, we protect against them in many ways,
    ///      better safe than sorry.
    /// @param by The account initializing deposits.
    function initializeDeposits(address by) external returns (bool);

    /// @notice Returns the decimals of the cToken.
    /// @dev We pull directly from underlying incase its a proxy contract,
    ///      and changes decimals on us.
    /// @return The number of decimals for this cToken,
    ///         matching the underlying token.
    function decimals() external view returns (uint8);

    function name() external view returns (string memory);
    function symbol() external view returns (string memory);

    /// @notice Returns whether the underlying token can be borrowed.
    /// @dev true = Borrowable; false = Not Borrowable.
    /// @return Whether this token is borrowable or not.
    function isBorrowable() external view returns (bool);

    /// @notice The token balance of an account.
    /// @dev Account address => account token balance.
    /// @param account The address of the account to query token balance for.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Returns the address of the underlying asset.
    /// @return The address of the underlying asset.
    function asset() external view returns (address);

    /// @notice Updates pending assets and returns a snapshot of the cToken
    ///         and `account` data.
    /// @dev Used by marketManager to more efficiently perform
    ///      liquidity checks.
    /// @return result The snapshot of the cToken and `account` data.
    function getSnapshotUpdated(
        address account
    ) external returns (AccountSnapshot memory result);

    /// @notice Get a snapshot of `account` data in this Curvance token.
    /// @dev NOTE: Does not accrue pending assets as part of the call.
    /// @param account The address of the account to snapshot.
    /// @return The account snapshot of `account`.
    function getSnapshot(
        address account
    ) external view returns (AccountSnapshot memory);

    /// @notice Total number of cTokens in circulation.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the total amount of assets held by the market.
    /// @return The total amount of assets held by the market.
    function totalAssets() external view returns (uint256);

    /// @notice Address of the Market Manager linked to this contract.
    function marketManager() external view returns (IMarketManager);

    /// @notice Returns the amount of assets that would be exchanged
    ///         by the vault for `shares` provided.
    /// @param shares The number of shares to theoretically use
    ///               for conversion to assets.
    /// @return The number of assets a user would receive for converting
    ///         `shares`.
    function convertToAssets(uint256 shares) external view returns (uint256);

    /// @notice Updates pending assets and returns the up-to-date exchange
    ///         rate from the underlying to the BorrowableCToken.
    /// @dev Oracle Manager calculates cToken value from this exchange rate.
    /// @return The share -> asset exchange rate, in `WAD`.
    function exchangeRateUpdated() external returns (uint256) ;

    /// @notice Returns the up-to-date exchange rate from the underlying to
    ///         the BorrowableCToken.
    /// @dev Oracle Manager calculates cToken value from this exchange rate.
    /// @return The share -> asset exchange rate, in `WAD`.
    function exchangeRate() external view returns (uint256);

    /// @notice Executes multiple calls in a single transaction.
    ///         This can be used to update oracle prices before
    ///         a liquidity dependent action.
    function multicall(
        Multicall.MulticallAction[] memory calls
    ) external returns (bytes[] memory results);

    /// @notice Caller deposits `assets` into the market and `receiver`
    ///         receives shares.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function deposit(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Caller deposits `assets` into the market, `receiver` receives
    ///         shares, and collateralization of `assets` is enabled.
    /// @dev The caller must be depositing for themselves, or be managing
    ///      their position through a Position Manager contract.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function depositAsCollateral(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Caller deposits `assets` into the market, `receiver` receives
    ///         shares, and collateralization of `assets` is enabled.
    /// @dev Requires that `receiver` approves the caller prior to
    ///      collateralize on their behalf.
    ///      NOTE: Be careful who you approve here!
    ///      They can delay redemption of assets through repeated
    ///      collateralization preventing withdrawal.
    /// @param assets The amount of the underlying assets to deposit.
    /// @param receiver The account that should receive the shares.
    /// @return shares The amount of shares received by `receiver`.
    function depositAsCollateralFor(
        uint256 assets,
        address receiver
    ) external returns (uint256 shares);

    /// @notice Withdraws assets, quoted in `shares` from the market,
    ///         and burns `owner` shares.
    /// @dev Does not force collateral to be withdrawn.
    /// @param shares The amount of shares to be redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner`.
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Withdraws assets, quoted in `shares` from the market,
    ///         and burns `owner` shares, sending assets to `receiver`.
    /// @dev Does not force collateral to be withdrawn.
    /// @param shares The amount of shares to redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner` and sent to
    ///                `receiver`.
    function redeemFor(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Caller withdraws assets from the market and burns their
    ///         shares, on behalf of `owner`.
    /// @dev Forces collateral to be withdrawn from `owner` collateralPosted.
    /// @param shares The amount of shares to redeemed.
    /// @param receiver The account that should receive the assets.
    /// @param owner The account that will burn their shares to withdraw
    ///              assets.
    /// @return assets The amount of assets redeemed by `owner` and sent to
    ///                `receiver`.
    function redeemCollateralFor(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    /// @notice Used by a Position Manager contract to redeem assets from
    ///         collateralized shares by `account` to perform a complex
    ///         action.
    /// @param assets The amount of the underlying assets to redeem.
    /// @param owner The owner address of assets to redeem.
    /// @param action Instructions for a deleverage action containing:
    ///               cToken Address of the cToken that will be redeemed from
    ///                      and assets swapped into `borrowableCToken` asset.
    ///               collateralAssets The amount of `cToken` that will be
    ///                                deleveraged, in assets.
    ///               borrowableCToken Address of the borrowableCToken that
    ///                                will have its debt paid.
    ///               repayAssets The amount of `borrowableCToken` asset that
    ///                           will be repaid to lenders.
    ///               swapActions Swap actions instructions converting
    ///                           collateral asset into debt asset to
    ///                           facilitate deleveraging.
    ///               auxData Optional auxiliary data for execution of a
    ///                       deleverage action.
    function withdrawByPositionManager(
        uint256 assets,
        address owner,
        IPositionManager.DeleverageAction memory action
    ) external;

    /// @notice Amount of tokens that has been posted as collateral,
    ///         in shares.
    function marketCollateralPosted() external view returns (uint256);

    /// @notice Shares of this token that an account has posted as collateral.
    /// @param account The address of the account to check collateral posted
    ///                of.
    function collateralPosted(address account) external view returns (uint256);

    /// @notice Transfers tokens from `account` to `liquidator`.
    /// @dev Will fail unless called by a cToken during the process
    ///      of liquidation.
    /// @param shares An array containing the number of cToken shares
    ///               to seize.
    /// @param liquidator The account receiving seized cTokens.
    /// @param accounts An array containing the accounts having
    ///                 collateral seized.
    function seize(
        uint256[] calldata shares,
        address liquidator,
        address[] calldata accounts
    ) external;

    /// @notice Allows users to simulate the effects of their deposit at
    ///         the current block.
    /// @param assets The number of assets to preview a deposit call.
    /// @return The shares received for depositing `assets`.
    function previewDeposit(uint256 assets) external view returns (uint256);
}

// contracts/interfaces/IExternalCalldataChecker.sol

interface IExternalCalldataChecker {
    /// @notice Inspects calldata for compliance with other swap instruction
    ///         parameters.
    /// @dev Used on swap to inspect and validate calldata safety.
    /// @param swapAction Swap action instructions including both direct
    ///                   parameters and decodeable calldata.
    /// @param expectedRecipient Address who will receive proceeds of
    ///                          `swapAction`.
    function checkCalldata(
        SwapperLib.Swap memory swapAction,
        address expectedRecipient
    ) external;
}

// contracts/interfaces/IOracleManager.sol

interface IOracleManager {
    /// @notice Retrieves the price of a specified asset from either single
    ///         or dual oracles.
    /// @dev If the asset has one oracle, it fetches the price from a single feed.
    ///      If it has two or more oracles, it fetches the price from both feeds.
    /// @param asset The address of the asset to retrieve the price for.
    /// @param inUSD Whether the price should be returned in USD or ETH.
    /// @param getLower Whether the lower or higher price should be returned
    ///                 if two feeds are available.
    /// @return price The current price of `asset`.
    /// @return errorCode An error code related to fetching the price:
    ///                   '0' indicates no error fetching price.
    ///                   '1' indicates that price should be taken with
    ///                   caution.
    ///                   '2' indicates a complete failure in receiving
    ///                   a price.
    function getPrice(
        address asset,
        bool inUSD,
        bool getLower
    ) external view returns (uint256 price, uint256 errorCode);

    /// @notice Retrieves the prices of a collateral token and debt token
    ///         underlyings.
    /// @param collateralToken The cToken currently collateralized to price.
    /// @param debtToken The cToken borrowed from to price.
    /// @param errorCodeBreakpoint The error code that will cause liquidity
    ///                            operations to revert.
    /// @return collateralUnderlyingPrice The current price of
    ///                                   `collateralToken` underlying.
    /// @return debtUnderlyingPrice The current price of `debtToken`
    ///                             underlying.
    function getPriceIsolatedPair(
        address collateralToken,
        address debtToken,
        uint256 errorCodeBreakpoint
    ) external returns (uint256, uint256);

    /// @notice Retrieves the prices and account data of multiple assets
    ///         inside a Curvance Market.
    /// @param account The account to retrieve data for.
    /// @param assets An array of asset addresses to retrieve the prices for.
    /// @param errorCodeBreakpoint The error code that will cause liquidity
    ///                            operations to revert.
    /// @return AccountSnapshot[] Contains `assets` data for `account`
    /// @return uint256[] Contains prices for `assets`.
    /// @return uint256 The number of assets `account` is in.
    function getPricesForMarket(
        address account,
        address[] calldata assets,
        uint256 errorCodeBreakpoint
    )
        external
        returns (AccountSnapshot[] memory, uint256[] memory, uint256);

    /// @notice Potentially removes the dependency on pricing from `adaptor`
    ///         for `asset`, triggered by an adaptor's notification of a price
    ///         feed's removal.
    /// @notice Removes a pricing adaptor for `asset` triggered by an
    ///         adaptor's notification of a price feed's removal.
    /// @dev Requires that the adaptor is currently being used for pricing
    ///      for `asset`.
    ///      NOTE: This intentionally does not modify asset deviation values
    ///            because they simply wont be used if there are less than two
    ///            pricing adaptors in use, so no reason to delete data as
    ///            when a second pricing adaptor is configured the deviation
    ///            has the opportunity be to reconfigured anyway.
    /// @param asset The address of the asset to potentially remove the
    ///              pricing adaptor dependency from depending on current
    ///              `asset` configuration.
    function notifyFeedRemoval(address asset) external;

    /// @notice Returns the adaptors used for pricing `asset`.
    /// @param asset The address of the asset to get pricing adaptors for.
    /// @return The current adaptor(s) used for pricing `asset`.
    function getPricingAdaptors(
        address asset
    ) external view returns(address[] memory);

    /// @notice Address => Adaptor approval status.
    /// @param adaptor The address of the adaptor to check.
    /// @return True if the adaptor is supported, false otherwise.
    function isApprovedAdaptor(address adaptor) external view returns (bool);

    /// @notice Whether a token is recognized as a Curvance token or not,
    ///         if it is, will return its underlying asset address instead
    ///         of address (0).
    /// @return The cToken's underlying asset, or address(0) if not a cToken.
    function cTokens(address cToken) external view returns (address);

    /// @notice Checks if a given asset is supported by the Oracle Manager.
    /// @dev An asset is considered supported if it has one
    ///      or more associated price feeds.
    /// @param asset The address of the asset to check.
    /// @return True if the asset is supported, false otherwise.
    function isSupportedAsset(address asset) external view returns (bool);

    /// @notice Check whether L2 sequencer is valid or down.
    /// @return True if sequencer is valid.
    function isSequencerValid() external view returns (bool);
}

// contracts/interfaces/IPositionManager.sol

interface IPositionManager {
    /// TYPES ///

    /// @notice Instructions for a leverage action.
    /// @param borrowableCToken Address of the borrowableCToken that will be
    ///                         borrowed from and assets swapped into `cToken`
    ///                         asset.
    /// @param borrowAssets The amount borrowed from `borrowableCToken`,
    ///                     in assets.
    /// @param cToken Curvance token assets that borrowed funds will be
    ///               swapped into.
    /// @param expectedShares The expected shares received from depositing
    ///                       into `cToken` with swapped `borrowAssets`.
    /// @param swapAction Swap action instructions converting debt asset into
    ///                   collateral asset to facilitate leveraging.
    /// @param auxData Optional auxiliary data for execution of a leverage
    ///                action.
    struct LeverageAction {
        IBorrowableCToken borrowableCToken;
        uint256 borrowAssets;
        ICToken cToken;
        uint256 expectedShares;
        SwapperLib.Swap swapAction;
        bytes auxData;
    }

    /// @notice Instructions for a deleverage action.
    /// @param cToken Address of the cToken that will be redeemed from and
    ///               assets swapped into `borrowableCToken` asset.
    /// @param collateralAssets The amount of `cToken` that will be
    ///                         deleveraged, in assets.
    /// @param borrowableCToken Address of the borrowableCToken that will
    ///                         have its debt paid.
    /// @param repayAssets The amount of `borrowableCToken` asset that will
    ///                    be repaid to lenders.
    /// @param swapActions Swap actions instructions converting collateral
    ///                    asset into debt asset to facilitate deleveraging.
    /// @param auxData Optional auxiliary data for execution of a deleverage
    ///                action.
    struct DeleverageAction {
        ICToken cToken;
        uint256 collateralAssets;
        IBorrowableCToken borrowableCToken;
        uint256 repayAssets;
        SwapperLib.Swap[] swapActions;
        bytes auxData;
    }

    /// @notice Callback function to execute post borrow of
    ///         `borrowableCToken`'s asset and swap it to deposit
    ///         new collateralized shares for `owner`.
    /// @dev Measures slippage after this callback validating that `owner`
    ///      is still within acceptable liquidity requirements.
    /// @param borrowableCToken The borrowableCToken borrowed from.
    /// @param borrowAssets The amount of `borrowableCToken`'s asset borrowed.
    /// @param owner The account borrowing that will be swapped into
    ///              collateral assets deposited into Curvance.
    /// @param action Instructions for a leverage action containing:
    ///               borrowableCToken Address of the borrowableCToken that
    ///                                will be borrowed from and assets
    ///                                swapped into `cToken` asset.
    ///               borrowAssets The amount borrowed from
    ///                            `borrowableCToken`, in assets.
    ///               cToken Curvance token assets that borrowed funds will be
    ///                      swapped into.
    ///               swapAction Swap action instructions converting debt
    ///                          asset into collateral asset to facilitate
    ///                          leveraging.
    ///               auxData Optional auxiliary data for execution of a
    ///                       leverage action.
    function onBorrow(
        address borrowableCToken,
        uint256 borrowAssets,
        address owner,
        LeverageAction memory action
    ) external;

    /// @notice Callback function to execute post redemption of `cToken`'s
    ///         asset and swap it to repay outstanding debt for `owner`.
    /// @dev Measures slippage after this callback validating that `owner`
    ///      is still within acceptable liquidity requirements.
    /// @param cToken The Curvance token redeemed for its underlying.
    /// @param collateralAssets The amount of `cToken` underlying redeemed.
    /// @param owner The account redeeming collateral that will be used to
    ///              repay their active debt.
    /// @param action Instructions for a deleverage action containing:
    ///               cToken Address of the cToken that will be redeemed from
    ///                      and assets swapped into `borrowableCToken` asset.
    ///               collateralAssets The amount of `cToken` that will be
    ///                                deleveraged, in assets.
    ///               borrowableCToken Address of the borrowableCToken that
    ///                                will have its debt paid.
    ///               repayAssets The amount of `borrowableCToken` asset that
    ///                           will be repaid to lenders.
    ///               swapAction Swap actions instructions converting
    ///                          collateral asset into debt asset to
    ///                          facilitate deleveraging.
    ///               auxData Optional auxiliary data for execution of a
    ///                       deleverage action.
    function onRedeem(
        address cToken,
        uint256 collateralAssets,
        address owner,
        DeleverageAction memory action
    ) external;
}

// contracts/libraries/SwapperLib.sol

/// @title Curvance Swapper Library.
/// @notice Helper Library for performing composable swaps with varying
///         degrees of slippage tolerance. "Unsafe" swaps perform a standard
///         slippage check whereas "safe" swaps not only check for standard
///         slippage but also check against the Oracle Manager's prices as
///         well.
///         NOTE: This library does not intend to provide support for fee on
///               transfer tokens though support may be built in the future.
library SwapperLib {
    /// TYPES ///

    /// @notice Instructions to execute a swap, selling `inputToken` for
    ///         `outputToken`.
    /// @param inputToken Address of input token to swap from.
    /// @param inputAmount The amount of `inputToken` to swap.
    /// @param outputToken Address of token to swap into.
    /// @param target Address of the swapper, usually an aggregator.
    /// @param slippage The amount of value-loss acceptable from swapping
    ///                 between tokens.
    /// @param call Swap instruction calldata.
    struct Swap {
        address inputToken;
        uint256 inputAmount;
        address outputToken;
        address target;
        uint256 slippage;
        bytes call;
    }

    /// @notice Address identifying a chain's native token.
    address public constant native =
        0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// ERRORS ///

    error SwapperLib__UnknownCalldata();
    error SwapperLib__TokenPrice(address inputToken);
    error SwapperLib__Slippage(uint256 slippage);
    error SwapperLib__SameTokens();

    /// INTERNAL FUNCTIONS ///

    /// @notice Swaps `action.inputToken` into a `action.outputToken`
    ///         without an extra slippage check.
    /// @param cr The address of the Protocol Central Registry to pull
    ///           addresses from.
    /// @param action Instructions for a swap action containing:
    ///               inputToken Address of input token to swap from.
    ///               inputAmount The amount of `inputToken` to swap.
    ///               outputToken Address of token to swap into.
    ///               target Address of the swapper, usually an aggregator.
    ///               slippage The amount of value-loss acceptable from
    ///                        swapping between tokens.
    ///               call Swap instruction calldata.
    /// @return outAmount The output amount received from swapping.
    function _swapUnsafe(
        ICentralRegistry cr,
        Swap memory action
    ) internal returns (uint256 outAmount) {
        address outputToken = action.outputToken;
        address inputToken = action.inputToken;

        // Do not use this library if the tokens are the same.
        if (CommonLib._isMatchingToken(inputToken, outputToken)) {
            revert SwapperLib__SameTokens();
        }

        address callDataChecker = cr.externalCalldataChecker(action.target);

        // Validate we know how to verify this calldata.
        if (callDataChecker == address(0)) {
            revert SwapperLib__UnknownCalldata();
        }

        // Verify calldata integrity.
        IExternalCalldataChecker(callDataChecker)
            .checkCalldata(action, address(this));

        // Approve `action.inputToken` to target contract, if necessary.
        _approveIfNeeded(inputToken, action.target, action.inputAmount);

        // Cache output token from struct for easier querying.
        
        uint256 balanceBefore = CommonLib._balanceOf(outputToken);

        uint256 callValue = CommonLib._isNative(inputToken) ?
            action.inputAmount : 0;

        // Execute the swap.
        LowLevelCallsHelper._callWithNative(
            action.target,
            action.call,
            callValue
        );

        // Remove any excess approval.
        _removeApprovalIfNeeded(inputToken, action.target);

        outAmount = CommonLib._balanceOf(outputToken) - balanceBefore;
    }

    /// @notice Swaps `action.inputToken` into a `action.outputToken`
    ///         with an extra slippage check.
    /// @param cr The address of the Protocol Central Registry to pull
    ///           addresses from.
    /// @param action Instructions for a swap action containing:
    ///                   inputToken Address of input token to swap from.
    ///                   inputAmount The amount of `inputToken` to swap.
    ///                   outputToken Address of token to swap into.
    ///                   target Address of the swapper, usually an
    ///                          aggregator.
    ///                   slippage The amount of value-loss acceptable from
    ///                            swapping between tokens.
    ///                   call Swap instruction calldata.
    /// @return outAmount The output amount received from swapping.
    function _swapSafe(
        ICentralRegistry cr,
        Swap memory action
    ) internal returns (uint256 outAmount) {
        if (action.slippage >= WAD) {
            revert SwapperLib__Slippage(action.slippage);
        }

        outAmount = _swapUnsafe(cr, action);

        IOracleManager om = CommonLib._oracleManager(cr);
        uint256 valueIn = _getValue(om, action.inputToken, action.inputAmount);
        uint256 valueOut = _getValue(om, action.outputToken, outAmount);

        // Check if swap received positive slippage.
        if (valueOut > valueIn) {
            return outAmount;
        }

        // Calculate % slippage from executed swap.
        uint256 slippage = FixedPointMathLib.mulDivUp(
            valueIn - valueOut,
            WAD,
            valueIn
        );

        if (slippage > action.slippage) {
            revert SwapperLib__Slippage(slippage);
        }
    }

    /// @notice Get the value of a token amount.
    /// @notice Approves `token` spending allowance, if needed.
    /// @param om The Oracle Manager address to call for pricing `token`.
    /// @param token The token address to get the value of.
    /// @param amount The amount of `token` to get the value of.
    function _getValue(
        IOracleManager om,
        address token,
        uint256 amount
    ) internal view returns (uint256 result) {
        // If token is native, normalize to address(0) so it is compatible 
        // with the Oracle Manager.
        if (token == address(0)) {
            token = native;
        }
        
        (uint256 price, uint256 errorCode) = om.getPrice(token, true, true);
        if (errorCode != NO_ERROR) {
            revert SwapperLib__TokenPrice(token);
        }

        // Return price in WAD form.
        result = FixedPointMathLib.mulDiv(
            price,
            amount,
            10 ** (CommonLib._isNative(token) ? 18 : IERC20(token).decimals())
        );
    }

    /// @notice Approves `token` spending allowance, if needed.
    /// @param token The token address to approve.
    /// @param spender The spender address.
    /// @param amount The approval amount.
    function _approveIfNeeded(
        address token,
        address spender,
        uint256 amount
    ) internal {
        if (!CommonLib._isNative(token)) {
            SafeTransferLib.safeApprove(token, spender, amount);
        }
    }

    /// @notice Removes `token` spending allowance, if needed.
    /// @param token The token address to remove approval.
    /// @param spender The spender address.
    function _removeApprovalIfNeeded(address token, address spender) internal {
        if (!CommonLib._isNative(token)) {
            if (IERC20(token).allowance(address(this), spender) > 0) {
                SafeTransferLib.safeApprove(token, spender, 0);
            }
        }
    }
}

// contracts/market/DynamicIRM.sol

/// @title Curvance Dynamic Interest Rate Model.
/// @notice Manages borrow and lending interest rates for borrowable Curvance
///         tokens.
/// @dev A dynamically adjusting interest rate model built to incentivize
///      growth, and minimize liquidity crunches.
///
///      At its core the Curvance Dynamic Interest Rate Model uses two
///      different interest rates:
///      The "baseRatePerSecond" linearly increases interest until
///      `vertexStart` is reached, where `vertexRatePerSecond` then is
///      used instead. This behaves very similar to the classic "Jump Rate"
///      interest rate model just without the risk-free rate.
///
///      This model then builds on top of the previous systems by introducing
///      a dynamic "Vertex Multiplier" which increases the skew of
///      `vertexRatePerSecond`. The Vertex Multiplier is adjusted upward or
///      downward based on the utilization of liquidity inside the
///      borrowableCToken pool.
///
///      This means that if utilization remains elevated during update
///      periods, the interest rate paid by borrowers will continually
///      increase. This will, in theory, attract new lenders who require
///      a higher yield to provide liquidity to a particular market.
///      At the same time, higher borrow rates incentivize interest rate
///      sensitive borrowers to repay their outstanding debt.
///      These two actions both will decrease net liquidity utilization,
///      decreasing borrow rates, and with a heavy enough drop, begin
///      decreasing the Vertex Multiplier.
///
///      This process is optimized by the introduction of a decay mechanism.
///      When the Vertex Multiplier is elevated, the decay rate naturally
///      reduces the excess skew overtime. This has the effect of creating a
///      "downward sloping" interest rate model. From a mathematical sense,
///      this means when the multiplier value is elevated, a constant negative
///      velocity is applied to it, regardless of positive or negative
///      acceleration applied due to liquidity utilization. By having a
///      naturally decreasing interest rate model users are incentivized to
///      continually borrow from the borrowableCToken market over other
///      solutions. Then, when liquidity dries up, the interest rate model
///      attracts new lenders. The combination of these two forces should,
///      in theory, create an efficient system that naturally stimulates
///      market growth while also decreasing the risk of liquidity crunches.
///
///      The Vertex Multiplier adjustment logic is as follows:
///
///      When utilization is below `vertexStart`:
///         If the utilization is below the 'decreaseThresholdEnd',
///         decay rate and maximum adjustment velocity is applied.
///         For higher utilizations (but still below the vertex),
///         a new multiplier is calculated by applying a negative curve value
///         to the adjustment. This new multiplier is also subjected to the
///         decay multiplier.
///
///      When utilization is above `vertexStart`:
///         If the utilization rate is below the 'increaseThresholdStart',
///         it simply applies the decay to the current multiplier.
///         If the utilization is higher, it calculates a new multiplier by
///         applying a positive curve value to the adjustment. This adjustment
///         is also subjected to the decay multiplier.
///
///      NOTE: The Dynamic Interest Rate model will not be able to update its
///            modifier until a borrowable Curvance token is properly linked
///            to it via setlinkedToken().
///
///            If a borrowable Curvance token updates to another dynamic
///            interest rate model contract then this contract theoretically
///            can still be called by it afterwards if the smart contract was
///            malformed, this does not really have any tangible impact but
///            for developers who may adapt this smart contract in the future,
///            I figure its worth mentioning.
///
contract DynamicIRM is IDynamicIRM, ERC165 {
    /// TYPES ///

    /// @title Rates Configuration.
    /// @notice Stores configuration data for current Dynamic Interest
    ///         Rate Model.
    /// @dev Once this token is set it can never be changed, like an immutable
    ///      variable, this IRM will also be depreciated if that token ever
    ///      switches IRMs.
    /// @param baseRatePerSecond Rate at which interest is accumulated,
    ///                          before `vertexStart`, per second, in `WAD`.
    /// @param vertexRatePerSecond Rate at which interest is
    ///                            accumulated, after `vertexStart`,
    ///                            per second, in `WAD`.
    /// @param vertexStart Utilization rate point where vertex rate
    ///                    is used, instead of base rate, in `WAD`.
    /// @param increaseThresholdStart The utilization rate at which the vertex
    ///                               multiplier will begin to increase,
    ///                               in 10 * `BPS`.
    /// @param decreaseThresholdEnd The utilization rate at which the vertex
    ///                             multiplier negative velocity will max out,
    ///                             in 10 * `BPS`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decayPerAdjustment The rate at which `vertexMultiplier` will
    ///                           decay back down per `adjustmentRate`,
    ///                           in `BPS`.
    /// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
    ///                            can be, in `multiplier` denomination
    ///                            aka `WAD`.
    /// @param linkedToken The borrowable Curvance token linked to this
    ///                    interest rate model contract.
    struct RatesConfig {
        uint64 baseRatePerSecond;
        uint64 vertexRatePerSecond;
        uint64 vertexStart;
        uint24 increaseThresholdStart;
        uint24 decreaseThresholdEnd;
        uint16 adjustmentVelocity;
        uint16 decayPerAdjustment;
        uint80 vertexMultiplierMax;
        address linkedToken;
    }

    /// CONSTANTS ///

    /// @notice The interval at which interest rates are adjusted, in seconds.
    /// @dev 10 minutes = 600 seconds.
    ///      The 10 minute adjustment rate is set so that borrowers experience
    ///      on average 2 interest rate adjustments from a standard borrow
    ///      action (20 minute minimum holding period).
    uint256 public constant ADJUSTMENT_RATE = 10 minutes;
    
    /// @notice Curvance DAO hub.
    ICentralRegistry public immutable centralRegistry;

    /// @notice The maximum value that the annual base interest rate can
    ///         be set to, in `WAD`.
    ///         E.g. 1.5 * WAD = 150% Base Interest Rate value at
    ///         `vertexStart` % borrowing utilization.
    uint256 internal constant _MAX_BASE_INTEREST_RATE_PER_YEAR = 1.5e18;
    /// @notice The maximum value that the annual vertex interest rate can
    ///         be set to, in `WAD`.
    ///         E.g. 2 * WAD = 200% Vertex Interest Rate value at
    ///         100% borrowing utilization.
    uint256 internal constant _MAX_VERTEX_INTEREST_RATE_PER_YEAR = 2e18;
    /// @notice The maximum value that the vertex interest rate can
    ///         be set to begin at, in `WAD`.
    ///         E.g. 0.99 * WAD = Vertex rate begins at 99% utilization.
    uint256 internal constant _MAX_VERTEX_START = 0.99e18;
    /// @notice The minimum value that the vertex interest rate can
    ///         be set to begin at, in `WAD`.
    ///         E.g. 0.50 * WAD = Vertex rate begins at 50% utilization.
    uint256 internal constant _MIN_VERTEX_START = 0.5e18;
    /// @notice The maximum rate at which `vertexMultiplier` is adjusted,
    ///         in BPS on top of base rate (1 `BPS`).
    ///         E.g. 1 * BPS = 120% multiplied to vertex interest rate per
    ///         `ADJUSTMENT_RATE` at 100% utilization.
    uint256 internal constant _MAX_VERTEX_ADJUSTMENT_VELOCITY = 2000;
    /// @notice The minimum rate at which `vertexMultiplier` is adjusted,
    ///         in BPS on top of base rate (1 `BPS`).
    ///         E.g. 0.01 * BPS = 101% multiplied to vertex interest rate per
    ///         `ADJUSTMENT_RATE` at 100% utilization.
    uint256 internal constant _MIN_VERTEX_ADJUSTMENT_VELOCITY = 100;
        /// @notice Maximum Rate at which `vertexMultiplier` will
    ///         decay per `ADJUSTMENT_RATE`, in `BPS`.
    /// @dev 200 = 2%.
    uint256 internal constant _MAX_VERTEX_DECAY_RATE = 200;
    /// @notice The maximum value that `vertexMultiplierMax` can be set
    ///         to, in `WAD`.
    ///         E.g. 1 * WAD = 100% Maximum `vertexMultiplierMax` value.
    uint256 internal constant _MAXIMUM_VERTEX_MULTIPLIER_MAX = 50e18;
    /// @notice The minimum value that `vertexMultiplierMax` can be set to,
    ///         in `WAD`.
    ///         E.g. 1 * WAD = 100% Minimum `vertexMultiplierMax` value.
    uint256 internal constant _MINIMUM_VERTEX_MULTIPLIER_MAX = WAD;

    /// @notice Very small adjustments in utilization can lose 1 decimal of
    ///         precision when calculating inflection points with `BPS`
    ///         denomination so we use 10 * BPS instead for
    ///         `increaseThresholdStart` and `decreaseThresholdEnd`.
    uint256 internal constant _INFLECTION_DENOMINATION = 1e13;
    /// STORAGE ///

    /// @notice The dynamic value applied to `vertexRatePerSecond`, increasing
    ///         it as `utilizationRate` remains elevated over time, adjusted
    ///         every `ADJUSTMENT_RATE`, in `WAD`.
    uint256 public vertexMultiplier;

    /// @notice Struct containing current configuration data for the
    ///         dynamic interest rate model.
    RatesConfig public ratesConfig;

    /// EVENTS ///

    event NewIRM(RatesConfig config);
    event TokenLinked(address borrowableCToken);

    /// ERRORS ///

    error DynamicIRM__Unauthorized();
    error DynamicIRM__InvalidToken();
    error DynamicIRM__InvalidUtilizationStart();
    error DynamicIRM__InvalidInterestRatePerYear();
    error DynamicIRM__InvalidAdjustmentVelocity();
    error DynamicIRM__InvalidDecayRate();
    error DynamicIRM__InvalidMultiplierMax();

    /// CONSTRUCTOR ///

    /// @param cr The address of the Protocol Central Registry.
    /// @param baseRatePerYear Rate at which interest is accumulated,
    ///                        before `vertexStart`, per year,
    ///                        in `BPS`.
    /// @param vertexRatePerYear Rate at which interest is accumulated,
    ///                          after `vertexStart`, per year,
    ///                          in `BPS`.
    /// @param vertexStart The utilization point at which the vertex
    ///                    rate is applied, in `BPS`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decayPerAdjustment The rate at which `vertexMultiplier` will
    ///                           decay back down per `adjustmentRate`,
    ///                           in `BPS`.
    /// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
    ///                            can be, in `BPS`.
    constructor(
        ICentralRegistry cr,
        uint256 baseRatePerYear,
        uint256 vertexRatePerYear,
        uint256 vertexStart,
        uint256 adjustmentVelocity,
        uint256 decayPerAdjustment,
        uint256 vertexMultiplierMax
    ) {
        CentralRegistryLib._isCentralRegistry(cr);
        centralRegistry = cr;

        _updateDynamicIRM(
            baseRatePerYear,
            vertexRatePerYear,
            vertexStart,
            adjustmentVelocity,
            decayPerAdjustment,
            vertexMultiplierMax,
            true
        );
    }

    /// EXTERNAL FUNCTIONS ///

    /// @notice The borrowable Curvance token linked to this interest rate
    ///         model contract.
    /// @dev Once this token is set it can never be changed, like an immutable
    ///      variable, this IRM will also be depreciated if that token ever
    ///      switches IRMs.
    /// @return result The linked borrowableCToken address.
    function linkedToken() external view returns(address result) {
        result = ratesConfig.linkedToken;
    }

    /// @notice Sets the dynamic interest rate model's linked borrowable
    ///         Curvance token (cToken) which interest rates this contract
    ///         will manage.
    /// @dev Once this function is properly it can never be called again.
    /// @param cTokenAddress The address of the token to be linked
    ///                      to this interest rate model contract.
    function setLinkedToken(address cTokenAddress) external {
        if (!centralRegistry.hasDaoPermissions(msg.sender)) {
            revert DynamicIRM__Unauthorized();
        }

        RatesConfig storage c = ratesConfig;

        // Validate that a borrowable Curvance token has not already been
        // linked to this smart contract.
        if (c.linkedToken != address(0)) {
            revert DynamicIRM__Unauthorized();
        }

        // Validate that the token being linked is actually a borrowable token
        // if the token is not a Curvance token this will also natively fail,
        // which is fine too.
        if (!IBorrowableCToken(cTokenAddress).isBorrowable()) {
            revert DynamicIRM__InvalidToken();
        }

        // Validate that the token is actually expecting this interest
        // rate model to be linked to it.
        if (address(IBorrowableCToken(cTokenAddress).IRM()) != address(this)) {
            revert DynamicIRM__InvalidToken();
        }

        c.linkedToken = cTokenAddress;

        emit TokenLinked(cTokenAddress);
    }

    /// @notice Updates the dynamic interest rate model's configuration
    ///         for calculating interest payment for `linkedToken`.
    /// @param baseRatePerYear Rate at which interest is accumulated,
    ///                        before `vertexStart`, per year, in `BPS`.
    /// @param vertexRatePerYear Rate at which interest is accumulated,
    ///                          after `vertexStart`, per year, in `BPS`.
    /// @param vertexStart The utilization point at which the vertex
    ///                    rate is applied, in `BPS`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decayPerAdjustment The rate at which `vertexMultiplier` will
    ///                           decay back down per `adjustmentRate`,
    ///                           in `BPS`.
    /// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
    ///                            can be, in `BPS`.
    /// @param vertexReset Whether `vertexMultiplier` should be reset back
    ///                    to its default value.
    function updateDynamicIRM(
        uint256 baseRatePerYear,
        uint256 vertexRatePerYear,
        uint256 vertexStart,
        uint256 adjustmentVelocity,
        uint256 decayPerAdjustment,
        uint256 vertexMultiplierMax,
        bool vertexReset
    ) external {
        _checkMarketPermissions();

        address linkedCToken = ratesConfig.linkedToken;
        if (linkedCToken != address(0)) {
            IBorrowableCToken(linkedCToken).accrueIfNeeded();
        }

        _updateDynamicIRM(
            baseRatePerYear,
            vertexRatePerYear,
            vertexStart,
            adjustmentVelocity,
            decayPerAdjustment,
            vertexMultiplierMax,
            vertexReset
        );
    }

    /// @notice Calculates the interest rate paid per second by borrowers,
    ///         in percentage paid, per second, in `WAD`, and updates
    ///         `vertexMultiplier` if necessary.
    /// @dev NOTE: Updates to `vertexMultiplier` are applied to `ratesPerSecond`
    ///            AFTER the next adjustment, this is to prevent someone flash
    ///            borrowing/lending a ton of capital and moving rates.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return ratePerSecond The interest rate paid per second by borrowers,
    ///                       in percentage paid, per second, in `WAD`.
    /// @return adjustmentRate The period of time at which interest rates are
    ///                        adjusted, in seconds.
    function adjustedBorrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) external returns (uint256 ratePerSecond, uint256 adjustmentRate) {
        RatesConfig memory c = ratesConfig;

        // Validate that the linked token itself is calling to update
        // its interest accrued.
        if (msg.sender != c.linkedToken) {
            revert DynamicIRM__Unauthorized();
        }

        uint256 util = utilizationRate(assetsHeld, debt);
        uint256 vertexPoint = c.vertexStart;
        uint256 multiplier = vertexMultiplier;
        bool belowVertex = (util <= vertexPoint);
        
        // Pull current interest rate.
        if (belowVertex) {
            ratePerSecond = _baseRate(util, c.baseRatePerSecond);
        } else {
            ratePerSecond = _vertexRate(
                util,
                c.baseRatePerSecond,
                c.vertexRatePerSecond,
                vertexPoint,
                multiplier
            );
        }

        // If `vertexMultiplier` is already at its minimum,
        // and would decrease more, can break here.
        // Convert `increaseThresholdStart` to `WAD` to be in same terms as
        // `util`, no precision loss as a result.
        if (
            multiplier == WAD &&
            util < (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))
        ) {
            return (ratePerSecond, ADJUSTMENT_RATE);
        }

        /// Vertex Multiplier adjustment cases below:

        /// Case 1: Vertex Multiplier downward adjustment.
        if (belowVertex) {
            vertexMultiplier = _updateBelowVertex(c, util, multiplier);
            return (ratePerSecond, ADJUSTMENT_RATE);
        }

        /// Case 2: Vertex Multiplier upward adjustment.
        vertexMultiplier = _updateAboveVertex(c, util, multiplier);
        adjustmentRate = ADJUSTMENT_RATE;
    }

    /// PUBLIC FUNCTIONS ///

    /// @notice Calculates the current borrow rate per second,
    ///         with updated `vertexMultiplier` applied.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return result The borrow rate percentage per second, in `WAD`.
    function predictedBorrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) public view returns (uint256 result) {
        uint256 util = utilizationRate(assetsHeld, debt);
        RatesConfig memory c = ratesConfig;
        uint256 vertexStart = c.vertexStart;

        // Directly pull interest rate since `vertexMultiplier` is irrelevant
        // with util <= vertexStart.
        if (util <= vertexStart) {
            return _baseRate(util, c.baseRatePerSecond);
        }

        uint256 multiplier = vertexMultiplier;
        // Vertex multiplier is not going to change so we can pull interest
        // rate with current `vertexMultiplier`.
        // Convert `increaseThresholdStart` to `WAD` to be in same terms as
        // `util`, no precision loss as a result.
        if (
            multiplier == WAD &&
            util < (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))
        ) {
            return _vertexRate(
                util,
                c.baseRatePerSecond,
                c.vertexRatePerSecond,
                vertexStart,
                multiplier
            );
        }

        // Get updated vertex multiplier then pull interest rate with new
        // vertex multiplier.
        result = _vertexRate(
            util,
            c.baseRatePerSecond,
            c.vertexRatePerSecond,
            vertexStart,
            _updateAboveVertex(c, util, multiplier)
        );
    }

    /// @notice Calculates the current borrow rate, per second.
    /// @dev This function's intention is for frontend data querying and
    ///     should not be used for onchain execution.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return r The borrow interest rate percentage, per second, in `WAD`.
    function borrowRate(
        uint256 assetsHeld,
        uint256 debt
    ) public view returns (uint256 r) {
        uint256 util = utilizationRate(assetsHeld, debt);
        // Cache from storage since we only need to query a new config values.
        RatesConfig storage c = ratesConfig;
        uint256 vertexStart = c.vertexStart;

        if (util <= vertexStart) {
            return _baseRate(util, c.baseRatePerSecond);
        }

        r = _vertexRate(
            util,
            c.baseRatePerSecond,
            c.vertexRatePerSecond,
            vertexStart,
            vertexMultiplier
        );
    }

    /// @notice Calculates the current supply rate, per second.
    /// @dev This function's intention is for frontend data querying and
    ///     should not be used for onchain execution.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @param interestFee The current interest rate protocol fee
    ///                    for the market token, in `BPS`.
    /// @return r The supply interest rate percentage, per second, in `WAD`.
    function supplyRate(
        uint256 assetsHeld,
        uint256 debt,
        uint256 interestFee
    ) public view returns (uint256 r) {
        // feeAdjustedBorrowRate = (borrowRate * (1 - Interest Fee)) / BPS.
        uint256 feeAdjustedBorrowRate =  _mulDiv(
            borrowRate(assetsHeld, debt),
            BPS - interestFee,
            BPS
        );

        // Supply Rate = (utilizationRate * feeAdjustedBorrowRate) / WAD.
        r = _mulDiv(
            utilizationRate(assetsHeld, debt),
            feeAdjustedBorrowRate,
            WAD
        );
    }

    /// @notice Calculates the borrow utilization rate of the market.
    /// @param assetsHeld The amount of underlying assets held in the pool.
    /// @param debt The amount of outstanding debt in the pool.
    /// @return r The utilization rate between [0, WAD].
    function utilizationRate(
        uint256 assetsHeld,
        uint256 debt
    ) public pure returns (uint256 r) {
        // Utilization rate is 0 when there are no outstanding debt.
        r = debt == 0 ? 0 : _mulDiv(debt, WAD, assetsHeld + debt);
    }

    /// @inheritdoc ERC165
    /// @param interfaceId The interface ID to check.
    /// @return result Whether the contract implements the interface.
    function supportsInterface(
        bytes4 interfaceId
    ) public view override returns (bool result) {
        result = interfaceId == type(IDynamicIRM).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /// INTERNAL FUNCTIONS ///

    /// @notice Calculates the interest rate for `util` market utilization.
    /// @param util The utilization rate of the market, in `WAD`.
    /// @return r The calculated base interest rate, in `WAD`.
    function _baseRate(
        uint256 util,
        uint256 baseRatePerSecond
    ) internal pure returns (uint256 r) {
        r = _mulDiv(util, baseRatePerSecond, WAD);
    }

    /// @notice Calculates the interest rate under `vertexRatePerSecond`
    ///         conditions, e.g. `util` > `vertex ` based on market
    ///         utilization.
    /// @param util The utilization rate of the market above
    ///            `vertexStart`, in `WAD`.
    /// @param vertexStart The point at which utilization begins
    ///                    calculating interest rate paid off
    ///                    `vertexRatePerSecond` * `multiplier`.
    /// @param multiplier The multiplicative value applied to 
    ///                   `vertexRatePerSecond`, in `WAD`.
    /// @return r The calculated vertex interest rate, in `WAD`.
    function _vertexRate(
        uint256 util,
        uint256 baseRatePerSecond,
        uint256 vertexRatePerSecond,
        uint256 vertexStart,
        uint256 multiplier
    ) internal pure returns (uint256 r) {
        r = _mulDiv(vertexStart, baseRatePerSecond, WAD) +
            _mulDiv(
            util - vertexStart,
            vertexRatePerSecond * multiplier,
            WAD_SQUARED
        );
    }

    /// @notice Updates the parameters of the dynamic interest rate model
    ///         used in the market.
    /// @dev This function sets various parameters for the dynamic interest
    ///      rate model, adjusting how interest rates are calculated based
    ///      on system utilization.
    ///      Emits a {NewIRM} event.
    /// @param baseRatePerYear Rate at which interest is accumulated,
    ///                        before `vertexStart`, per year,
    ///                        in `BPS`.
    /// @param vertexRatePerYear Rate at which interest is accumulated,
    ///                          after `vertexStart`, per year,
    ///                          in `BPS`.
    /// @param vertexStart The utilization point at which the vertex
    ///                            rate is applied, in `BPS`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decayPerAdjustment The rate at which `vertexMultiplier` will
    ///                           decay back down per `adjustmentRate`,
    ///                           in `BPS`.
    /// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
    ///                            can be, in `BPS`.
    /// @param vertexReset A boolean flag indicating whether `vertexMultiplier`
    ///                    should be reset to `WAD`.
    function _updateDynamicIRM(
        uint256 baseRatePerYear,
        uint256 vertexRatePerYear,
        uint256 vertexStart,
        uint256 adjustmentVelocity,
        uint256 decayPerAdjustment,
        uint256 vertexMultiplierMax,
        bool vertexReset
    ) internal {
        // Convert the parameters from `BPS` to `WAD` format.
        // While inefficient, we want to minimize potential human error
        // as much as possible, even if it costs a bit extra gas on config.
        baseRatePerYear = _bpToWad(baseRatePerYear);
        vertexRatePerYear = _bpToWad(vertexRatePerYear);
        vertexStart = _bpToWad(vertexStart);
        vertexMultiplierMax = _bpToWad(vertexMultiplierMax);

        /// Validate config values are within allowed constraints.
        if (
            vertexStart > _MAX_VERTEX_START ||
            vertexStart < _MIN_VERTEX_START
        ) {
            revert DynamicIRM__InvalidUtilizationStart();
        }

        if (
            baseRatePerYear > _MAX_BASE_INTEREST_RATE_PER_YEAR ||
            vertexRatePerYear > _MAX_VERTEX_INTEREST_RATE_PER_YEAR
        ) {
            revert DynamicIRM__InvalidInterestRatePerYear();
        }

        if (decayPerAdjustment > _MAX_VERTEX_DECAY_RATE) {
            revert DynamicIRM__InvalidDecayRate();
        }
        
        if (
            adjustmentVelocity > _MAX_VERTEX_ADJUSTMENT_VELOCITY ||
            adjustmentVelocity < _MIN_VERTEX_ADJUSTMENT_VELOCITY
        ) {
            revert DynamicIRM__InvalidAdjustmentVelocity();
        }

        if (
            vertexMultiplierMax > _MAXIMUM_VERTEX_MULTIPLIER_MAX ||
            vertexMultiplierMax < _MINIMUM_VERTEX_MULTIPLIER_MAX
        ) {
            revert DynamicIRM__InvalidMultiplierMax();
        }

        RatesConfig storage config = ratesConfig;

        config.baseRatePerSecond = uint64(_mulDiv(
            baseRatePerYear,
            WAD,
            SECONDS_PER_YEAR * vertexStart
        ));

        config.vertexRatePerSecond = uint64(_mulDiv(
            vertexRatePerYear,
            WAD,
            SECONDS_PER_YEAR * (WAD - vertexStart)
        ));

        config.vertexStart = uint64(vertexStart);
        config.adjustmentVelocity = uint16(adjustmentVelocity);
        config.decayPerAdjustment = uint16(decayPerAdjustment);
        config.vertexMultiplierMax = uint80(vertexMultiplierMax);
        if (vertexReset) {
            vertexMultiplier = WAD;
        } else {
            uint256 cachedVertexMultiplier = vertexMultiplier;
            // If the new `vertexMultiplierMax` is below `vertexMultiplier`
            // we should clamp it down to the new maximum.
            if (vertexMultiplierMax < cachedVertexMultiplier) {
                vertexMultiplier = vertexMultiplierMax;
            }
        }

        // Dynamic rates start increasing halfway between desired
        // utilization and 100% utilization, in `WAD`.
        uint256 thresholdLength = (WAD - vertexStart) / 2;

        // Dynamic rates start increasing as soon as we are halfway between
        // `vertexStart` and 100% utilization. Convert to `BPS` form to save a
        // storage slot and `vertexStart` is set in `BPS` so we shouldnt lose
        // precision.
        config.increaseThresholdStart =
            uint24((vertexStart + thresholdLength) / _INFLECTION_DENOMINATION);

        // Dynamic rates start decreasing as soon as we are below desired
        // utilization (vertexStart) and maximizes an equal utilization down
        // from where increase threshold starts upward. Convert to `BPS` form
        // to save a storage slot and `vertexStart` is set in `BPS` so we
        // shouldnt lose precision.
        config.decreaseThresholdEnd =
            uint24((vertexStart - thresholdLength) / _INFLECTION_DENOMINATION);

        emit NewIRM(config);
    }

    /// @notice Calculates and returns the updated multiplier for scenarios
    ///         where the utilization rate is above the vertex.
    /// @dev This function is used to adjust `vertexMultiplier` based on
    ///      the borrowableCToken's current borrow utilization.
    ///      A decay mechanism is incorporated to gradually decrease the
    ///      multiplier, and ensures the multiplier does not fall below 1,
    ///      in WAD.
    ///      NOTE: The multiplier is updated with the following logic:
    ///      If the utilization is below the 'increaseThresholdStart',
    ///      it simply applies the decay to the current multiplier.
    ///      If the utilization is higher, it calculates a new multiplier by
    ///      applying a positive curve value to the adjustment.
    ///      This adjustment is also subjected to the decay multiplier.
    /// @param c The cached version of the current `RatesConfig`.
    /// @param util The current utilization value, used to determine how the
    ///             multiplier should be adjusted.
    /// @param multiplier The dynamic value applied to `vertexRatePerSecond`
    ///                   that will be adjusted, in `WAD`.
    /// @return newMultiplier The updated multiplier after applying decay
    ///                       and adjustments based on the current utilization
    ///                       level.
    function _updateAboveVertex(
        RatesConfig memory c,
        uint256 util,
        uint256 multiplier
    ) internal pure returns (uint256 newMultiplier) {
        // Calculate decay rate.
        uint256 decay = _mulDiv(multiplier, c.decayPerAdjustment, BPS);

        if (util <= (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))) {
            newMultiplier = multiplier - decay;

            // Check if decay rate sends new rate below 1.
            return newMultiplier < WAD ? WAD : newMultiplier;
        }

        // Apply a positive multiplier to the current multiplier based on
        // `util` vs `increaseThresholdStart` and `WAD`.
        // Then apply decay effect.
        newMultiplier = _positiveShift(
            multiplier, // `multiplier` in `WAD`.
            c.adjustmentVelocity, // `adjustmentVelocity` in `BPS`.
            decay, // `decay` in `multiplier` aka `WAD`.
            util, // `current` in `WAD`.
            _INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart) // `start` convert to WAD to match util.
        );

        // Update and return with adjustment and decay rate applied.
        // Its theorectically possible for the multiplier to be below 1
        // due to decay, so we need to check like in below vertex.
        if (newMultiplier < WAD) {
            return WAD;
        }

        // Make sure `newMultiplier` is not above `vertexMultiplierMax`.
        newMultiplier = newMultiplier < c.vertexMultiplierMax
            ? newMultiplier
            : c.vertexMultiplierMax;
    }

    /// @notice Calculates and returns the updated multiplier for scenarios
    ///         where the utilization rate is below the vertex.
    /// @dev This function is used to adjust `vertexMultiplier` based on
    ///      the borrowableCToken's current borrow utilization.
    ///      A decay mechanism is incorporated to gradually decrease the
    ///      multiplier, and ensures the multiplier does not fall below 1,
    ///      in WAD.
    ///      NOTE: The multiplier is updated with the following logic:
    ///      If the utilization is below the 'decreaseThresholdEnd',
    ///      decay rate and maximum adjustment velocity is applied.
    ///      For higher utilizations (but still below the vertex),
    ///      a new multiplier is calculated by applying a negative curve value
    ///      to the adjustment. This new multiplier is also subjected to the
    ///      decay multiplier.
    /// @param c The cached version of the current `RatesConfig`.
    /// @param util The current utilization value, used to determine how the
    ///             multiplier should be adjusted.
    /// @param multiplier The dynamic value applied to `vertexRatePerSecond`
    ///                   that will be adjusted, in `WAD`.
    /// @return newMultiplier The updated multiplier after applying decay
    ///                       and adjustments based on the current utilization
    ///                       level.
    function _updateBelowVertex(
        RatesConfig memory c,
        uint256 util,
        uint256 multiplier
    ) internal pure returns (uint256 newMultiplier) {
        // Calculate decay rate.
        uint256 decay = _mulDiv(multiplier, c.decayPerAdjustment, BPS);

        // Convert `decreaseThresholdEnd` to `WAD` to be in same terms as
        // `util`, no precision loss as a result.
        if (util <= (_INFLECTION_DENOMINATION * uint256(c.decreaseThresholdEnd))) {
            // Apply maximum adjustVelocity reduction (shift = 1).
            // We only need to adjust for `BPS` precision since `shift`
            // is not used here.
            // currentMultiplier / (1 + adjustmentVelocity) = newMultiplier.
            newMultiplier = _mulDiv(
                multiplier,
                BPS,
                BPS + c.adjustmentVelocity
            ) - decay;

            // Check if decay rate sends multiplier below 1.
            return newMultiplier < WAD ? WAD : newMultiplier;
        }

        // Apply a negative multiplier to the current multiplier based on
        // `util` vs `vertexStart` and `decreaseThresholdEnd`.
        // Then apply decay effect.
        newMultiplier = _negativeShift(
            multiplier, // `multiplier` in `WAD`.
            c.adjustmentVelocity, // `adjustmentVelocity` in `BPS`.
            decay, // `decay` in `multiplier` aka `WAD`.
            util, // `current` in `WAD`.
            c.vertexStart, // `start` in `WAD`.
            _INFLECTION_DENOMINATION * uint256(c.decreaseThresholdEnd) // `end` convert to WAD to match util/vertexStart.
        );

        // Update and return with adjustment and decay rate applied.
        // But first check if new rate sends multiplier below 1.
        return newMultiplier < WAD ? WAD : newMultiplier;
    }

    /// @notice Calculates positive shift value based on `current`,
    ///         `start`, and `end` values. Then applies the linear curve
    ///         effect to the adjustment velocity, then applies the
    ///         adjustment velocity and decay effect to `multiplier`.
    /// @dev The shift is scaled by current, start, and end values and
    ///      multiplied by `WAD` to maintain precision. This results in 1,
    ///      (in `WAD`) if the current value is greater than or equal to
    ///      `end`. The terminal shift is then applied to the
    ///      adjustmentVelocity in determining the growth of `multiplier` for
    ///      this period, then the decay rate is applied.
    /// @param multiplier The dynamic value applied to `vertexRatePerSecond`
    ///                   that will be adjusted, in `WAD`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decay The current decay rate, calculated as a negative % value,
    ///              in `multiplier` denomination, aka `WAD` form.
    /// @param current The current value, representing a point on the curve.
    /// @param start The start value of the curve, marking the beginning of
    ///              the calculation range.
    /// @return result The new multiplier with the calculated shift value,
    ///                and decay rate applied.
    function _positiveShift(
        uint256 multiplier,
        uint256 adjustmentVelocity,
        uint256 decay,
        uint256 current, // `util`.
        uint256 start // `increaseThresholdStart`.
    ) internal pure returns (uint256 result) {
        // We do not need to check for current >= end, since we know util is
        // the absolute maximum utilization is 100%, and thus current == end.
        // Which will result in WAD result for `shift`.
        // Thus, this will be bound between [0, WAD].
        uint256 shift = _mulDiv(current - start, WAD, WAD - start);

        // Apply `shift` result to adjustment velocity.
        // Then add 100% on top for final adjustment value to `multiplier`.
        // We use WAD_BPS here since `shift` is in `WAD` for max precision but
        // `adjustmentVelocity` is in BPS since it does not need greater
        // precision due to being configured in `BPS`.
        shift = WAD_BPS + (shift * adjustmentVelocity);

        // Apply positive `shift` effect to `currentMultiplier`, and
        // adjust for 1e36 precision. Then apply decay effect.
        result = _mulDiv(multiplier, shift, WAD_BPS) -  decay;
    }

    /// @notice Calculates negative shift value based on `current`,
    ///         `start`, and `end` values. Then applies the linear curve
    ///         effect to the adjustment velocity, then applies the
    ///         adjustment velocity and decay effect to `multiplier`.
    /// @dev The shift is scaled by current, start, and end values and
    ///      multiplied by `WAD` to maintain precision. This results in 1,
    ///      (in `WAD`) if the current value is less than or equal to
    ///      `end`. The terminal shift is then applied to the
    ///      adjustmentVelocity in determining the reduction of `multiplier`
    ///      for this period, then the decay rate is applied.
    /// @param multiplier The dynamic value applied to `vertexRatePerSecond`
    ///                   that will be adjusted, in `WAD`.
    /// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
    ///                           is adjusted per `adjustmentRate`, in `BPS`.
    /// @param decay The current decay rate, calculated as a negative % value,
    ///              in `multiplier` denomination, aka `WAD` form.
    /// @param current The current value, representing a point on the curve.
    /// @param start The start value of the curve, marking the beginning of
    ///              the calculation range, equal to `vertexStart`.
    /// @param end The end value of the curve, marking the end of the
    ///            calculation range.
    /// @return result The new multiplier with the calculated shift value,
    ///                and decay rate applied.
    function _negativeShift(
        uint256 multiplier,
        uint256 adjustmentVelocity,
        uint256 decay,
        uint256 current, // `util`.
        uint256 start, // `vertexStart`.
        uint256 end // `decreaseThresholdEnd`.
    ) internal pure returns (uint256 result) {
        // Calculate linear curve multiplier. We know that current > end,
        // based on pre conditional checks.
        // Thus, this will be bound between [0, WAD].
        uint256 shift = _mulDiv(start - current, WAD, start - end);

        // Apply `shift` result to adjustment velocity.
        // Then add 100% on top for final adjustment value to `multiplier`.
        // We use WAD_BPS here since `shift` is in `WAD` for max precision but
        // `adjustmentVelocity` is in BPS since it does not need greater
        // precision due to being configured in `BPS`.
        shift = WAD_BPS + (shift * adjustmentVelocity);

        // Apply negative `shift` effect to `currentMultiplier`, and
        // adjust for 1e36 precision. Then apply decay effect.
        result = _mulDiv(multiplier, WAD_BPS, shift) -  decay;
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function _mulDiv(
        uint256 x,
        uint256 y,
        uint256 d
    ) internal pure returns (uint256 z) {
        z = FixedPointMathLib.mulDiv(x, y, d);
    }

    /// @dev Checks whether the caller has sufficient permissioning.
    function _checkMarketPermissions() internal view virtual {
        if (!centralRegistry.hasMarketPermissions(msg.sender)) {
            revert DynamicIRM__Unauthorized();
        }
    }

    /// @notice Multiplies `value` by 1e14 to convert it from `BPS`
    ///         to `WAD`.
    /// @dev Internal helper function for easily converting between scalars.
    /// @param value The value to convert from `BPS` to `WAD`.
    /// @return result The value, in `WAD`.
    function _bpToWad(uint256 value) internal pure returns (uint256 result) {
        result = value * 1e14;
    }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract ICentralRegistry","name":"cr","type":"address"},{"internalType":"uint256","name":"baseRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexStart","type":"uint256"},{"internalType":"uint256","name":"adjustmentVelocity","type":"uint256"},{"internalType":"uint256","name":"decayPerAdjustment","type":"uint256"},{"internalType":"uint256","name":"vertexMultiplierMax","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CentralRegistryLib__InvalidCentralRegistry","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidAdjustmentVelocity","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidDecayRate","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidInterestRatePerYear","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidMultiplierMax","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidToken","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidUtilizationStart","type":"error"},{"inputs":[],"name":"DynamicIRM__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint64","name":"baseRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexStart","type":"uint64"},{"internalType":"uint24","name":"increaseThresholdStart","type":"uint24"},{"internalType":"uint24","name":"decreaseThresholdEnd","type":"uint24"},{"internalType":"uint16","name":"adjustmentVelocity","type":"uint16"},{"internalType":"uint16","name":"decayPerAdjustment","type":"uint16"},{"internalType":"uint80","name":"vertexMultiplierMax","type":"uint80"},{"internalType":"address","name":"linkedToken","type":"address"}],"indexed":false,"internalType":"struct DynamicIRM.RatesConfig","name":"config","type":"tuple"}],"name":"NewIRM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrowableCToken","type":"address"}],"name":"TokenLinked","type":"event"},{"inputs":[],"name":"ADJUSTMENT_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"adjustedBorrowRate","outputs":[{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"adjustmentRate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"borrowRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"centralRegistry","outputs":[{"internalType":"contract ICentralRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkedToken","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"predictedBorrowRate","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ratesConfig","outputs":[{"internalType":"uint64","name":"baseRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexStart","type":"uint64"},{"internalType":"uint24","name":"increaseThresholdStart","type":"uint24"},{"internalType":"uint24","name":"decreaseThresholdEnd","type":"uint24"},{"internalType":"uint16","name":"adjustmentVelocity","type":"uint16"},{"internalType":"uint16","name":"decayPerAdjustment","type":"uint16"},{"internalType":"uint80","name":"vertexMultiplierMax","type":"uint80"},{"internalType":"address","name":"linkedToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cTokenAddress","type":"address"}],"name":"setLinkedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"interestFee","type":"uint256"}],"name":"supplyRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexStart","type":"uint256"},{"internalType":"uint256","name":"adjustmentVelocity","type":"uint256"},{"internalType":"uint256","name":"decayPerAdjustment","type":"uint256"},{"internalType":"uint256","name":"vertexMultiplierMax","type":"uint256"},{"internalType":"bool","name":"vertexReset","type":"bool"}],"name":"updateDynamicIRM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"utilizationRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"vertexMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

0x60a060405234801561000f575f5ffd5b506040516119df3803806119df83398101604081905261002e9161055e565b61003787610060565b6001600160a01b0387166080526100548686868686866001610091565b50505050505050610631565b610071816399011ef160e01b610439565b61008e576040516369b5e45b60e11b815260040160405180910390fd5b50565b61009a8761045d565b96506100a58661045d565b95506100b08561045d565b94506100bb8261045d565b9150670dbd2fc137a300008511806100da57506706f05b59d3b2000085105b156100f8576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b1600008711806101155750671bc16d674ec8000086115b15610133576040516332defd0160e01b815260040160405180910390fd5b60c88311156101555760405163c709b1f760e01b815260040160405180910390fd5b6107d08411806101655750606484105b1561018357604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b18800008211806101a15750670de0b6b3a764000082105b156101bf5760405163872fb21d60e01b815260040160405180910390fd5b60016101e188670de0b6b3a76400006101dc896301e133806105d5565b61046e565b81546001600160401b0319166001600160401b039190911617815561022187670de0b6b3a764000061021389826105ec565b6101dc906301e133806105d5565b8154600160401b600160c01b031916680100000000000000006001600160401b0392831602600160801b600160c01b03191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166001600160601b031990921691909117620100006001600160501b0386160217905581156102c057670de0b6b3a76400005f556102d1565b5f54808410156102cf575f8490555b505b5f60026102e688670de0b6b3a76400006105ec565b6102f091906105ff565b90506509184e72a000610303828961061e565b61030d91906105ff565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a00061033c82896105ec565b61034691906105ff565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061042690849081546001600160401b03811682526101208201906001600160401b03604082901c1660208401526001600160401b03608082901c16604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c08401526001600160501b03601082901c1660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f61044383610482565b8015610454575061045483836104b4565b90505b92915050565b5f61045782655af3107a40006105d5565b5f61047a84848461053a565b949350505050565b5f610494826301ffc9a760e01b6104b4565b801561045757506104ad826001600160e01b03196104b4565b1592915050565b6040516001600160e01b0319821660248201525f90819060440160408051601f19818403018152919052602080820180516001600160e01b03166301ffc9a760e01b17815282519293505f9283928392909183918a617530fa92503d91505f519050828015610524575060208210155b801561052f57505f81115b979650505050505050565b5f825f1904841183021582026105575763ad251c275f526004601cfd5b5091020490565b5f5f5f5f5f5f5f60e0888a031215610574575f5ffd5b87516001600160a01b038116811461058a575f5ffd5b602089015160408a015160608b015160808c015160a08d015160c0909d0151949e939d50919b909a50909850965090945092505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610457576104576105c1565b81810381811115610457576104576105c1565b5f8261061957634e487b7160e01b5f52601260045260245ffd5b500490565b80820180821115610457576104576105c1565b6080516113886106575f395f8181610158015281816103400152610c5601526113885ff3fe608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c8063ad58472611610088578063e46023e211610063578063e46023e2146101cb578063ea12489f146101de578063ec2de40c146101f6578063f3129fac14610209575f5ffd5b8063ad58472614610192578063c0b36044146101a5578063da6008f4146101b8575f5ffd5b806301ffc9a7146100cf5780630e53bfc4146100f75780632c2650721461010d578063589dc77b1461012257806367e860cd1461014a5780638f73dcfa14610153575b5f5ffd5b6100e26100dd3660046111a2565b6102f5565b60405190151581526020015b60405180910390f35b6100ff5f5481565b6040519081526020016100ee565b61012061011b3660046111e0565b61032b565b005b6101356101303660046111fb565b610574565b604080519283526020830191909152016100ee565b6100ff61025881565b61017a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ee565b6100ff6101a03660046111fb565b61072e565b6100ff6101b336600461121b565b6107a9565b6100ff6101c63660046111fb565b6107f3565b6101206101d9366004611251565b61095f565b600254600160601b90046001600160a01b031661017a565b6100ff6102043660046111fb565b6109e8565b600154600254610283916001600160401b0380821692600160401b8304821692600160801b81049092169162ffffff600160c01b8204811692600160d81b83049091169161ffff600160f01b909104811691908116906001600160501b0362010000820416906001600160a01b03600160601b9091041689565b604080516001600160401b039a8b168152988a1660208a0152969098169587019590955262ffffff938416606087015291909216608085015261ffff91821660a08501521660c08301526001600160501b031660e08201526001600160a01b03909116610100820152610120016100ee565b5f6001600160e01b03198216638ec124b360e01b148061032557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516355eaece960e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063abd5d9d290602401602060405180830381865afa15801561038d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b191906112ac565b6103ce5760405163ce8f8fcb60e01b815260040160405180910390fd5b600254600190600160601b90046001600160a01b0316156104025760405163ce8f8fcb60e01b815260040160405180910390fd5b816001600160a01b03166345d7b97a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561043e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046291906112ac565b61047f57604051630b613edd60e21b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316631e75db166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e991906112c7565b6001600160a01b03161461051057604051630b613edd60e21b815260040160405180910390fd5b6001810180546bffffffffffffffffffffffff16600160601b6001600160a01b038516908102919091179091556040519081527fcaec6e139653a20e6ea7c3862d384166d94b04bd44cd59e9357b8ac1c28182fc9060200160405180910390a15050565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820192909252600160c01b820462ffffff9081166060830152600160d81b8304166080820152600160f01b90910461ffff90811660a083015260025490811660c08301526201000081046001600160501b031660e0830152600160601b90046001600160a01b031661010082018190525f91829133146106395760405163ce8f8fcb60e01b815260040160405180910390fd5b5f61064486866109e8565b60408301515f549192506001600160401b03169081831180159061067f5761067884865f01516001600160401b0316610a19565b96506106a8565b6106a584865f01516001600160401b031687602001516001600160401b03168686610a2d565b96505b670de0b6b3a7640000821480156106d7575060608501516106d49062ffffff166509184e72a0006112f6565b84105b156106ec575061025894506107279350505050565b801561070f576106fd858584610a77565b5f555061025894506107279350505050565b61071a858584610b68565b5f55506102589450505050505b9250929050565b5f5f61073a84846109e8565b6001805491925090600160801b90046001600160401b031680831161077957815461076f9084906001600160401b0316610a19565b9350505050610325565b81545f5461079f9185916001600160401b0380831692600160401b900416908590610a2d565b9695505050505050565b5f5f6107cc6107b8868661072e565b6107c48561271061130d565b612710610c2d565b90506107ea6107db86866109e8565b82670de0b6b3a7640000610c2d565b95945050505050565b5f5f6107ff84846109e8565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820183905262ffffff600160c01b820481166060840152600160d81b820416608083015261ffff600160f01b909104811660a083015260025490811660c08301526001600160501b036201000082041660e08301526001600160a01b03600160601b909104166101008201529192508083116108bd5761076f83835f01516001600160401b0316610a19565b5f54670de0b6b3a7640000811480156108ee575060608301516108eb9062ffffff166509184e72a0006112f6565b84105b156109245761091984845f01516001600160401b031685602001516001600160401b03168585610a2d565b945050505050610325565b61095484845f01516001600160401b031685602001516001600160401b03168561094f888a88610b68565b610a2d565b979650505050505050565b610967610c41565b600254600160601b90046001600160a01b031680156109cf57806001600160a01b031663775a814a6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156109b8575f5ffd5b505af11580156109ca573d5f5f3e3d5ffd5b505050505b6109de88888888888888610ce6565b5050505050505050565b5f8115610a1057610a0b82670de0b6b3a7640000610a068287611320565b610c2d565b610a12565b5f5b9392505050565b5f610a128383670de0b6b3a7640000610c2d565b5f610a5a610a3b848861130d565b610a4584876112f6565b6ec097ce7bc90715b34b9f1000000000610c2d565b610a6d8487670de0b6b3a7640000610c2d565b61079f9190611320565b5f5f610a8e838660c0015161ffff16612710610c2d565b9050846080015162ffffff166509184e72a000610aab91906112f6565b8411610b025780610acf846127108860a0015161ffff16612710610a069190611320565b610ad9919061130d565b9150670de0b6b3a76400008210610af05781610afa565b670de0b6b3a76400005b915050610a12565b610b3f838660a0015161ffff16838789604001516001600160401b03168a6080015162ffffff166509184e72a000610b3a91906112f6565b611099565b9150670de0b6b3a76400008210610b5657816107ea565b50670de0b6b3a7640000949350505050565b5f5f610b7f838660c0015161ffff16612710610c2d565b9050846060015162ffffff166509184e72a000610b9c91906112f6565b8411610bac57610ad9818461130d565b610bdb838660a0015161ffff168387896060015162ffffff166509184e72a000610bd691906112f6565b611109565b9150670de0b6b3a7640000821015610bfe57670de0b6b3a7640000915050610a12565b8460e001516001600160501b03168210610c25578460e001516001600160501b03166107ea565b509392505050565b5f610c3984848461116d565b949350505050565b604051630225e24360e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630225e24390602401602060405180830381865afa158015610ca3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cc791906112ac565b610ce45760405163ce8f8fcb60e01b815260040160405180910390fd5b565b610cef87611191565b9650610cfa86611191565b9550610d0585611191565b9450610d1082611191565b9150670dbd2fc137a30000851180610d2f57506706f05b59d3b2000085105b15610d4d576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b160000871180610d6a5750671bc16d674ec8000086115b15610d88576040516332defd0160e01b815260040160405180910390fd5b60c8831115610daa5760405163c709b1f760e01b815260040160405180910390fd5b6107d0841180610dba5750606484105b15610dd857604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b1880000821180610df65750670de0b6b3a764000082105b15610e145760405163872fb21d60e01b815260040160405180910390fd5b6001610e3188670de0b6b3a7640000610a06896301e133806112f6565b815467ffffffffffffffff19166001600160401b0391909116178155610e7287670de0b6b3a7640000610e64898261130d565b610a06906301e133806112f6565b815477ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039283160267ffffffffffffffff60801b191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166bffffffffffffffffffffffff1990921691909117620100006001600160501b038616021790558115610f2057670de0b6b3a76400005f55610f31565b5f5480841015610f2f575f8490555b505b5f6002610f4688670de0b6b3a764000061130d565b610f509190611333565b90506509184e72a000610f638289611320565b610f6d9190611333565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a000610f9c828961130d565b610fa69190611333565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061108690849081546001600160401b0381168252610120820190604081901c6001600160401b03166020840152608081901c6001600160401b0316604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c0840152601081901c6001600160501b031660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f806110bb6110a8868661130d565b670de0b6b3a7640000610a06868861130d565b90506110c787826112f6565b6110db9069021e19e0c9bab2400000611320565b9050856110f38969021e19e0c9bab240000084610c2d565b6110fd919061130d565b98975050505050505050565b5f8061112b611118848661130d565b670de0b6b3a7640000610a06868261130d565b905061113786826112f6565b61114b9069021e19e0c9bab2400000611320565b905084611163888369021e19e0c9bab2400000610c2d565b610954919061130d565b5f825f19048411830215820261118a5763ad251c275f526004601cfd5b5091020490565b5f61032582655af3107a40006112f6565b5f602082840312156111b2575f5ffd5b81356001600160e01b031981168114610a12575f5ffd5b6001600160a01b03811681146111dd575f5ffd5b50565b5f602082840312156111f0575f5ffd5b8135610a12816111c9565b5f5f6040838503121561120c575f5ffd5b50508035926020909101359150565b5f5f5f6060848603121561122d575f5ffd5b505081359360208301359350604090920135919050565b80151581146111dd575f5ffd5b5f5f5f5f5f5f5f60e0888a031215611267575f5ffd5b873596506020880135955060408801359450606088013593506080880135925060a0880135915060c088013561129c81611244565b8091505092959891949750929550565b5f602082840312156112bc575f5ffd5b8151610a1281611244565b5f602082840312156112d7575f5ffd5b8151610a12816111c9565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610325576103256112e2565b81810381811115610325576103256112e2565b80820180821115610325576103256112e2565b5f8261134d57634e487b7160e01b5f52601260045260245ffd5b50049056fea2646970667358221220583f06c7154160a501f609d0b7eb9479b0ce8c373f842ddc46da976b2b5996dc64736f6c634300081c00330000000000000000000000001310f352f1389969ece6741671c4b919523912ff00000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000005dc000000000000000000000000000000000000000000000000000000000000232800000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000186a0

Deployed Bytecode

0x608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c8063ad58472611610088578063e46023e211610063578063e46023e2146101cb578063ea12489f146101de578063ec2de40c146101f6578063f3129fac14610209575f5ffd5b8063ad58472614610192578063c0b36044146101a5578063da6008f4146101b8575f5ffd5b806301ffc9a7146100cf5780630e53bfc4146100f75780632c2650721461010d578063589dc77b1461012257806367e860cd1461014a5780638f73dcfa14610153575b5f5ffd5b6100e26100dd3660046111a2565b6102f5565b60405190151581526020015b60405180910390f35b6100ff5f5481565b6040519081526020016100ee565b61012061011b3660046111e0565b61032b565b005b6101356101303660046111fb565b610574565b604080519283526020830191909152016100ee565b6100ff61025881565b61017a7f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff81565b6040516001600160a01b0390911681526020016100ee565b6100ff6101a03660046111fb565b61072e565b6100ff6101b336600461121b565b6107a9565b6100ff6101c63660046111fb565b6107f3565b6101206101d9366004611251565b61095f565b600254600160601b90046001600160a01b031661017a565b6100ff6102043660046111fb565b6109e8565b600154600254610283916001600160401b0380821692600160401b8304821692600160801b81049092169162ffffff600160c01b8204811692600160d81b83049091169161ffff600160f01b909104811691908116906001600160501b0362010000820416906001600160a01b03600160601b9091041689565b604080516001600160401b039a8b168152988a1660208a0152969098169587019590955262ffffff938416606087015291909216608085015261ffff91821660a08501521660c08301526001600160501b031660e08201526001600160a01b03909116610100820152610120016100ee565b5f6001600160e01b03198216638ec124b360e01b148061032557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516355eaece960e11b81523360048201527f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff6001600160a01b03169063abd5d9d290602401602060405180830381865afa15801561038d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b191906112ac565b6103ce5760405163ce8f8fcb60e01b815260040160405180910390fd5b600254600190600160601b90046001600160a01b0316156104025760405163ce8f8fcb60e01b815260040160405180910390fd5b816001600160a01b03166345d7b97a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561043e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046291906112ac565b61047f57604051630b613edd60e21b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316631e75db166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e991906112c7565b6001600160a01b03161461051057604051630b613edd60e21b815260040160405180910390fd5b6001810180546bffffffffffffffffffffffff16600160601b6001600160a01b038516908102919091179091556040519081527fcaec6e139653a20e6ea7c3862d384166d94b04bd44cd59e9357b8ac1c28182fc9060200160405180910390a15050565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820192909252600160c01b820462ffffff9081166060830152600160d81b8304166080820152600160f01b90910461ffff90811660a083015260025490811660c08301526201000081046001600160501b031660e0830152600160601b90046001600160a01b031661010082018190525f91829133146106395760405163ce8f8fcb60e01b815260040160405180910390fd5b5f61064486866109e8565b60408301515f549192506001600160401b03169081831180159061067f5761067884865f01516001600160401b0316610a19565b96506106a8565b6106a584865f01516001600160401b031687602001516001600160401b03168686610a2d565b96505b670de0b6b3a7640000821480156106d7575060608501516106d49062ffffff166509184e72a0006112f6565b84105b156106ec575061025894506107279350505050565b801561070f576106fd858584610a77565b5f555061025894506107279350505050565b61071a858584610b68565b5f55506102589450505050505b9250929050565b5f5f61073a84846109e8565b6001805491925090600160801b90046001600160401b031680831161077957815461076f9084906001600160401b0316610a19565b9350505050610325565b81545f5461079f9185916001600160401b0380831692600160401b900416908590610a2d565b9695505050505050565b5f5f6107cc6107b8868661072e565b6107c48561271061130d565b612710610c2d565b90506107ea6107db86866109e8565b82670de0b6b3a7640000610c2d565b95945050505050565b5f5f6107ff84846109e8565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820183905262ffffff600160c01b820481166060840152600160d81b820416608083015261ffff600160f01b909104811660a083015260025490811660c08301526001600160501b036201000082041660e08301526001600160a01b03600160601b909104166101008201529192508083116108bd5761076f83835f01516001600160401b0316610a19565b5f54670de0b6b3a7640000811480156108ee575060608301516108eb9062ffffff166509184e72a0006112f6565b84105b156109245761091984845f01516001600160401b031685602001516001600160401b03168585610a2d565b945050505050610325565b61095484845f01516001600160401b031685602001516001600160401b03168561094f888a88610b68565b610a2d565b979650505050505050565b610967610c41565b600254600160601b90046001600160a01b031680156109cf57806001600160a01b031663775a814a6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156109b8575f5ffd5b505af11580156109ca573d5f5f3e3d5ffd5b505050505b6109de88888888888888610ce6565b5050505050505050565b5f8115610a1057610a0b82670de0b6b3a7640000610a068287611320565b610c2d565b610a12565b5f5b9392505050565b5f610a128383670de0b6b3a7640000610c2d565b5f610a5a610a3b848861130d565b610a4584876112f6565b6ec097ce7bc90715b34b9f1000000000610c2d565b610a6d8487670de0b6b3a7640000610c2d565b61079f9190611320565b5f5f610a8e838660c0015161ffff16612710610c2d565b9050846080015162ffffff166509184e72a000610aab91906112f6565b8411610b025780610acf846127108860a0015161ffff16612710610a069190611320565b610ad9919061130d565b9150670de0b6b3a76400008210610af05781610afa565b670de0b6b3a76400005b915050610a12565b610b3f838660a0015161ffff16838789604001516001600160401b03168a6080015162ffffff166509184e72a000610b3a91906112f6565b611099565b9150670de0b6b3a76400008210610b5657816107ea565b50670de0b6b3a7640000949350505050565b5f5f610b7f838660c0015161ffff16612710610c2d565b9050846060015162ffffff166509184e72a000610b9c91906112f6565b8411610bac57610ad9818461130d565b610bdb838660a0015161ffff168387896060015162ffffff166509184e72a000610bd691906112f6565b611109565b9150670de0b6b3a7640000821015610bfe57670de0b6b3a7640000915050610a12565b8460e001516001600160501b03168210610c25578460e001516001600160501b03166107ea565b509392505050565b5f610c3984848461116d565b949350505050565b604051630225e24360e01b81523360048201527f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff6001600160a01b031690630225e24390602401602060405180830381865afa158015610ca3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cc791906112ac565b610ce45760405163ce8f8fcb60e01b815260040160405180910390fd5b565b610cef87611191565b9650610cfa86611191565b9550610d0585611191565b9450610d1082611191565b9150670dbd2fc137a30000851180610d2f57506706f05b59d3b2000085105b15610d4d576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b160000871180610d6a5750671bc16d674ec8000086115b15610d88576040516332defd0160e01b815260040160405180910390fd5b60c8831115610daa5760405163c709b1f760e01b815260040160405180910390fd5b6107d0841180610dba5750606484105b15610dd857604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b1880000821180610df65750670de0b6b3a764000082105b15610e145760405163872fb21d60e01b815260040160405180910390fd5b6001610e3188670de0b6b3a7640000610a06896301e133806112f6565b815467ffffffffffffffff19166001600160401b0391909116178155610e7287670de0b6b3a7640000610e64898261130d565b610a06906301e133806112f6565b815477ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039283160267ffffffffffffffff60801b191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166bffffffffffffffffffffffff1990921691909117620100006001600160501b038616021790558115610f2057670de0b6b3a76400005f55610f31565b5f5480841015610f2f575f8490555b505b5f6002610f4688670de0b6b3a764000061130d565b610f509190611333565b90506509184e72a000610f638289611320565b610f6d9190611333565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a000610f9c828961130d565b610fa69190611333565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061108690849081546001600160401b0381168252610120820190604081901c6001600160401b03166020840152608081901c6001600160401b0316604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c0840152601081901c6001600160501b031660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f806110bb6110a8868661130d565b670de0b6b3a7640000610a06868861130d565b90506110c787826112f6565b6110db9069021e19e0c9bab2400000611320565b9050856110f38969021e19e0c9bab240000084610c2d565b6110fd919061130d565b98975050505050505050565b5f8061112b611118848661130d565b670de0b6b3a7640000610a06868261130d565b905061113786826112f6565b61114b9069021e19e0c9bab2400000611320565b905084611163888369021e19e0c9bab2400000610c2d565b610954919061130d565b5f825f19048411830215820261118a5763ad251c275f526004601cfd5b5091020490565b5f61032582655af3107a40006112f6565b5f602082840312156111b2575f5ffd5b81356001600160e01b031981168114610a12575f5ffd5b6001600160a01b03811681146111dd575f5ffd5b50565b5f602082840312156111f0575f5ffd5b8135610a12816111c9565b5f5f6040838503121561120c575f5ffd5b50508035926020909101359150565b5f5f5f6060848603121561122d575f5ffd5b505081359360208301359350604090920135919050565b80151581146111dd575f5ffd5b5f5f5f5f5f5f5f60e0888a031215611267575f5ffd5b873596506020880135955060408801359450606088013593506080880135925060a0880135915060c088013561129c81611244565b8091505092959891949750929550565b5f602082840312156112bc575f5ffd5b8151610a1281611244565b5f602082840312156112d7575f5ffd5b8151610a12816111c9565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610325576103256112e2565b81810381811115610325576103256112e2565b80820180821115610325576103256112e2565b5f8261134d57634e487b7160e01b5f52601260045260245ffd5b50049056fea2646970667358221220583f06c7154160a501f609d0b7eb9479b0ce8c373f842ddc46da976b2b5996dc64736f6c634300081c0033

Deployed Bytecode Sourcemap

130618:39656:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;150369:231;;;;;;:::i;:::-;;:::i;:::-;;;470:14:1;;463:22;445:41;;433:2;418:18;150369:231:0;;;;;;;;136522:31;;;;;;;;;643:25:1;;;631:2;616:18;136522:31:0;497:177:1;139748:1116:0;;;;;;:::i;:::-;;:::i;:::-;;143667:1922;;;;;;:::i;:::-;;:::i;:::-;;;;1592:25:1;;;1648:2;1633:18;;1626:34;;;;1565:18;143667:1922:0;1418:248:1;133304:52:0;;133346:10;133304:52;;133404:49;;;;;;;;-1:-1:-1;;;;;1859:32:1;;;1841:51;;1829:2;1814:18;133404:49:0;1671:227:1;147882:642:0;;;;;;:::i;:::-;;:::i;149073:593::-;;;;;;:::i;:::-;;:::i;145966:1494::-;;;;;;:::i;:::-;;:::i;142044:756::-;;;;;;:::i;:::-;;:::i;139237:112::-;139318:23;;-1:-1:-1;;;139318:23:0;;-1:-1:-1;;;;;139318:23:0;139237:112;;149945:262;;;;;;:::i;:::-;;:::i;136678:30::-;;;;;;;-1:-1:-1;;;;;136678:30:0;;;;-1:-1:-1;;;136678:30:0;;;;;-1:-1:-1;;;136678:30:0;;;;;;;-1:-1:-1;;;136678:30:0;;;;;-1:-1:-1;;;136678:30:0;;;;;;;-1:-1:-1;;;136678:30:0;;;;;;;;;;-1:-1:-1;;;;;136678:30:0;;;;;-1:-1:-1;;;;;;;;136678:30:0;;;;;;;;;;-1:-1:-1;;;;;4567:31:1;;;4549:50;;4635:31;;;4630:2;4615:18;;4608:59;4703:31;;;;4683:18;;;4676:59;;;;4783:8;4771:21;;;4766:2;4751:18;;4744:49;4830:21;;;;4824:3;4809:19;;4802:50;4901:6;4889:19;;;4883:3;4868:19;;4861:48;4946:19;4940:3;4925:19;;4918:48;-1:-1:-1;;;;;5003:35:1;4997:3;4982:19;;4975:64;-1:-1:-1;;;;;5076:32:1;;;5070:3;5055:19;;5048:61;4536:3;4521:19;136678:30:0;4194:921:1;150369:231:0;150462:11;-1:-1:-1;;;;;;150495:44:0;;-1:-1:-1;;;150495:44:0;;:97;;-1:-1:-1;;;;;;;;;;70939:40:0;;;150556:36;150486:106;150369:231;-1:-1:-1;;150369:231:0:o;139748:1116::-;139820:45;;-1:-1:-1;;;139820:45:0;;139854:10;139820:45;;;1841:51:1;139820:15:0;-1:-1:-1;;;;;139820:33:0;;;;1814:18:1;;139820:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;139815:112;;139889:26;;-1:-1:-1;;;139889:26:0;;;;;;;;;;;139815:112;140109:13;;139963:11;;-1:-1:-1;;;140109:13:0;;-1:-1:-1;;;;;140109:13:0;:27;140105:93;;140160:26;;-1:-1:-1;;;140160:26:0;;;;;;;;;;;140105:93;140423:13;-1:-1:-1;;;;;140405:45:0;;:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;140400:114;;140476:26;;-1:-1:-1;;;140476:26:0;;;;;;;;;;;140400:114;140704:4;-1:-1:-1;;;;;140645:64:0;140671:13;-1:-1:-1;;;;;140653:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;140645:64:0;;140641:130;;140733:26;;-1:-1:-1;;;140733:26:0;;;;;;;;;;;140641:130;140783:13;;;:29;;;;-1:-1:-1;;;;;;;;140783:29:0;;;;;;;;;;;;140830:26;;1841:51:1;;;140830:26:0;;1829:2:1;1814:18;140830:26:0;;;;;;;139804:1060;139748:1116;:::o;143667:1922::-;143830:34;;;;;;;;143853:11;143830:34;-1:-1:-1;;;;;143830:34:0;;;;;-1:-1:-1;;;143830:34:0;;;;;;;;-1:-1:-1;;;143830:34:0;;;;;;;;;;-1:-1:-1;;;143830:34:0;;;;;;;;;;-1:-1:-1;;;143830:34:0;;;;;;;-1:-1:-1;;;143830:34:0;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;143830:34:0;;;;;-1:-1:-1;;;143830:34:0;;-1:-1:-1;;;;;143830:34:0;;;;;;;-1:-1:-1;;;;143986:10:0;:27;143982:93;;144037:26;;-1:-1:-1;;;144037:26:0;;;;;;;;;;;143982:93;144087:12;144102:33;144118:10;144130:4;144102:15;:33::i;:::-;144168:13;;;;144146:19;144213:16;144087:48;;-1:-1:-1;;;;;;144146:35:0;;144260:19;;;;;;144341:331;;144389:36;144399:4;144405:1;:19;;;-1:-1:-1;;;;;144389:36:0;:9;:36::i;:::-;144373:52;;144341:331;;;144474:186;144504:4;144527:1;:19;;;-1:-1:-1;;;;;144474:186:0;144565:1;:21;;;-1:-1:-1;;;;;144474:186:0;144605:11;144635:10;144474:11;:186::i;:::-;144458:202;;144341:331;636:4;144944:10;:17;:103;;;;-1:-1:-1;145021:24:0;;;;144986:60;;145013:33;;136278:4;144986:60;:::i;:::-;144978:4;:69;144944:103;144926:199;;;-1:-1:-1;133346:10:0;;-1:-1:-1;145074:39:0;;-1:-1:-1;;;;145074:39:0;144926:199;145258:11;145254:156;;;145305:39;145324:1;145327:4;145333:10;145305:18;:39::i;:::-;145286:16;:58;-1:-1:-1;133346:10:0;;-1:-1:-1;145359:39:0;;-1:-1:-1;;;;145359:39:0;145254:156;145499:39;145518:1;145521:4;145527:10;145499:18;:39::i;:::-;145480:16;:58;-1:-1:-1;133346:10:0;;-1:-1:-1;;;;;143667:1922:0;;;;;;:::o;147882:642::-;147982:9;148004:12;148019:33;148035:10;148047:4;148019:15;:33::i;:::-;148167:11;148211:13;;148004:48;;-1:-1:-1;148167:11:0;-1:-1:-1;;;148211:13:0;;-1:-1:-1;;;;;148211:13:0;148241:19;;;148237:95;;148300:19;;148284:36;;148294:4;;-1:-1:-1;;;;;148300:19:0;148284:9;:36::i;:::-;148277:43;;;;;;;148237:95;148393:19;;;148489:16;148348:168;;148374:4;;-1:-1:-1;;;;;148393:19:0;;;;-1:-1:-1;;;148427:21:0;;;;148463:11;;148348;:168::i;:::-;148344:172;147882:642;-1:-1:-1;;;;;;147882:642:0:o;149073:593::-;149203:9;149302:29;149335:111;149357:28;149368:10;149380:4;149357:10;:28::i;:::-;149400:17;149406:11;745:3;149400:17;:::i;:::-;745:3;149335:7;:111::i;:::-;149302:144;;149538:120;149560:33;149576:10;149588:4;149560:15;:33::i;:::-;149608:21;636:4;149538:7;:120::i;:::-;149534:124;149073:593;-1:-1:-1;;;;;149073:593:0:o;145966:1494::-;146075:14;146102:12;146117:33;146133:10;146145:4;146117:15;:33::i;:::-;146161:34;;;;;;;;146184:11;146161:34;-1:-1:-1;;;;;146161:34:0;;;;;-1:-1:-1;;;146161:34:0;;;;;;;;-1:-1:-1;;;146161:34:0;;;;;;;;;;-1:-1:-1;;;146161:34:0;;;;;;;;-1:-1:-1;;;146161:34:0;;;;;;;;-1:-1:-1;;;146161:34:0;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;146161:34:0;;;;;;;;-1:-1:-1;;;;;;;;146161:34:0;;;;;;;;146102:48;;-1:-1:-1;146375:19:0;;;146371:95;;146418:36;146428:4;146434:1;:19;;;-1:-1:-1;;;;;146418:36:0;:9;:36::i;146371:95::-;146478:18;146499:16;636:4;146799:17;;:103;;;;-1:-1:-1;146876:24:0;;;;146841:60;;146868:33;;136278:4;146841:60;:::i;:::-;146833:4;:69;146799:103;146781:353;;;146936:186;146966:4;146989:1;:19;;;-1:-1:-1;;;;;146936:186:0;147027:1;:21;;;-1:-1:-1;;;;;146936:186:0;147067:11;147097:10;146936:11;:186::i;:::-;146929:193;;;;;;;;146781:353;147261:191;147287:4;147306:1;:19;;;-1:-1:-1;;;;;147261:191:0;147340:1;:21;;;-1:-1:-1;;;;;147261:191:0;147376:11;147402:39;147421:1;147424:4;147430:10;147402:18;:39::i;:::-;147261:11;:191::i;:::-;147252:200;145966:1494;-1:-1:-1;;;;;;;145966:1494:0:o;142044:756::-;142336:25;:23;:25::i;:::-;142397:23;;-1:-1:-1;;;142397:23:0;;-1:-1:-1;;;;;142397:23:0;142435:26;;142431:107;;142496:12;-1:-1:-1;;;;;142478:46:0;;:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;142431:107;142550:242;142582:15;142612:17;142644:11;142670:18;142703;142736:19;142770:11;142550:17;:242::i;:::-;142325:475;142044:756;;;;;;;:::o;149945:262::-;150050:9;150146;;:53;;150162:37;150170:4;636;150181:17;150170:4;150181:10;:17;:::i;:::-;150162:7;:37::i;:::-;150146:53;;;150158:1;150146:53;150142:57;149945:262;-1:-1:-1;;;149945:262:0:o;150850:179::-;150958:9;150984:37;150992:4;150998:17;636:4;150984:7;:37::i;151702:425::-;151909:9;151995:124;152017:18;152024:11;152017:4;:18;:::i;:::-;152050:32;152072:10;152050:19;:32;:::i;:::-;182:4;151995:7;:124::i;:::-;151935:44;151943:11;151956:17;636:4;151935:7;:44::i;:::-;:184;;;;:::i;162019:1860::-;162160:21;162228:13;162244:46;162252:10;162264:1;:20;;;162244:46;;745:3;162244:7;:46::i;:::-;162228:62;;162477:1;:22;;;162469:31;;136278:4;162442:58;;;;:::i;:::-;162433:4;:68;162429:623;;162908:5;162787:118;162813:10;745:3;162870:1;:20;;;162864:26;;745:3;162864:26;;;;:::i;162787:118::-;:126;;;;:::i;:::-;162771:142;;636:4;162999:13;:19;:41;;163027:13;162999:41;;;636:4;162999:41;162992:48;;;;;162429:623;163256:418;163285:10;163336:1;:20;;;163256:418;;163405:5;163463:4;163505:1;:13;;;-1:-1:-1;;;;;163256:418:0;163589:1;:22;;;163581:31;;136278:4;163554:58;;;;:::i;:::-;163256:14;:418::i;:::-;163240:434;;636:4;163830:13;:19;:41;;163858:13;163830:41;;;-1:-1:-1;636:4:0;;162019:1860;-1:-1:-1;;;;162019:1860:0:o;158957:1604::-;159098:21;159166:13;159182:46;159190:10;159202:1;:20;;;159182:46;;745:3;159182:7;:46::i;:::-;159166:62;;159289:1;:24;;;159281:33;;136278:4;159254:60;;;;:::i;:::-;159245:4;:70;159241:262;;159348:18;159361:5;159348:10;:18;:::i;159241:262::-;159701:361;159730:10;159781:1;:20;;;159701:361;;159850:5;159908:4;159985:1;:24;;;159977:33;;136278:4;159950:60;;;;:::i;:::-;159701:14;:361::i;:::-;159685:377;;636:4;160290:13;:19;160286:62;;;636:4;160326:10;;;;;160286:62;160466:1;:21;;;-1:-1:-1;;;;;160450:37:0;:13;:37;:103;;160532:1;:21;;;-1:-1:-1;;;;;160450:103:0;;;;159121:1440;158957:1604;;;;;:::o;169426:174::-;169533:9;169559:33;169584:1;169587;169590;169559:24;:33::i;:::-;169555:37;169426:174;-1:-1:-1;;;;169426:174:0:o;169678:190::-;169751:48;;-1:-1:-1;;;169751:48:0;;169788:10;169751:48;;;1841:51:1;169751:15:0;-1:-1:-1;;;;;169751:36:0;;;;1814:18:1;;169751:48:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;169746:115;;169823:26;;-1:-1:-1;;;169823:26:0;;;;;;;;;;;169746:115;169678:190::o;153588:3951::-;154112:25;154121:15;154112:8;:25::i;:::-;154094:43;;154168:27;154177:17;154168:8;:27::i;:::-;154148:47;;154220:21;154229:11;154220:8;:21::i;:::-;154206:35;;154274:29;154283:19;154274:8;:29::i;:::-;154252:51;;134307:7;154402:11;:31;:79;;;;134557:6;154450:11;:31;154402:79;154384:180;;;154515:37;;-1:-1:-1;;;154515:37:0;;;;;;;;;;;154384:180;133758:6;154594:15;:50;:121;;;;134060:4;154661:17;:54;154594:121;154576:225;;;154749:40;;-1:-1:-1;;;154749:40:0;;;;;;;;;;;154576:225;135436:3;154817:18;:43;154813:113;;;154884:30;;-1:-1:-1;;;154884:30:0;;;;;;;;;;;154813:113;134892:4;154964:18;:52;:121;;;;135228:3;155033:18;:52;154964:121;154946:224;;;155119:39;;-1:-1:-1;;;155119:39:0;;;;;;;;;;;154946:224;135683:5;155200:19;:52;:121;;;;636:4;155269:19;:52;155200:121;155182:219;;;155355:34;;-1:-1:-1;;;155355:34:0;;;;;;;;;;;155182:219;155442:11;155500:111;155522:15;636:4;155570:30;155589:11;1362:10;155570:30;:::i;155500:111::-;155466:146;;-1:-1:-1;;155466:146:0;-1:-1:-1;;;;;155466:146:0;;;;;;;155661:121;155683:17;636:4;155753:17;155759:11;636:4;155753:17;:::i;:::-;155733:38;;1362:10;155733:38;:::i;155661:121::-;155625:158;;-1:-1:-1;;155796:40:0;-1:-1:-1;;;;;;;;155625:158:0;;;;-1:-1:-1;;;;155796:40:0;;-1:-1:-1;;;155796:40:0;;;;;;;;-1:-1:-1;;;;;155847:54:0;-1:-1:-1;;;155847:54:0;;;;;;;;;;;;;;-1:-1:-1;155912:25:0;;:54;;;;;-1:-1:-1;;155977:56:0;;;;;;;;-1:-1:-1;;;;;155977:56:0;;;;;;156044:422;;;;636:4;156076:16;:22;156044:422;;;156131:30;156164:16;156336:44;;;156332:123;;;156401:16;:38;;;156332:123;156116:350;156044:422;156601:23;156649:1;156628:17;156634:11;636:4;156628:17;:::i;:::-;156627:23;;;;:::i;:::-;156601:49;-1:-1:-1;136278:4:0;156975:29;156601:49;156975:11;:29;:::i;:::-;156974:58;;;;:::i;:::-;156922:111;;;;;;;-1:-1:-1;;;156922:111:0;-1:-1:-1;;;;156922:111:0;;;;;;136278:4;157441:29;157455:15;157441:11;:29;:::i;:::-;157440:58;;;;:::i;:::-;157390:109;;;;;;;-1:-1:-1;;;157390:109:0;-1:-1:-1;;;;157390:109:0;;;;;;157517:14;;;;;;157390:6;;6658:13:1;;-1:-1:-1;;;;;6698:34:1;;3837:43;;6628:3;6613:19;;;6779:2;6775:18;;;-1:-1:-1;;;;;6771:43:1;6831:4;6816:20;;3837:43;6872:3;6868:19;;;-1:-1:-1;;;;;6864:44:1;6925:2;6910:18;;3837:43;6964:3;6960:19;;;6981:8;6956:34;7007:4;6992:20;;3944:33;7048:3;7044:19;;;7065:8;7040:34;7091:3;7076:19;;3944:33;7127:3;7123:19;;;7159:4;7144:20;;4041:31;-1:-1:-1;7211:4:1;7199:17;;7193:24;7261:6;7244:24;;7285:3;7270:19;;4041:31;7325:2;7321:20;;;-1:-1:-1;;;;;7317:49:1;7383:4;7368:20;;4136:47;7421:4;7417:22;;;7456:6;7441:22;;3521:44;7398:66;6435:1035;;;;;157517:14:0;;;;;;;;153870:3669;;153588:3951;;;;;;;:::o;168178:1143::-;168450:14;;168668:42;168676:15;168684:7;168676:5;:15;:::i;:::-;636:4;168698:11;168706:3;168698:5;:11;:::i;168668:42::-;168652:58;-1:-1:-1;169086:26:0;169094:18;168652:58;169086:26;:::i;:::-;169075:38;;546:4;169075:38;:::i;:::-;169067:46;;169308:5;169269:35;169277:10;546:4;169298:5;169269:7;:35::i;:::-;:44;;;;:::i;:::-;169260:53;168178:1143;-1:-1:-1;;;;;;;;168178:1143:0:o;165362:1199::-;165596:14;;165908:42;165916:15;165926:5;165916:7;:15;:::i;:::-;636:4;165938:11;165944:5;636:4;165938:11;:::i;165908:42::-;165892:58;-1:-1:-1;166326:26:0;166334:18;165892:58;166326:26;:::i;:::-;166315:38;;546:4;166315:38;:::i;:::-;166307:46;;166548:5;166509:35;166517:10;166529:5;546:4;166509:7;:35::i;:::-;:44;;;;:::i;7895:476::-;7967:9;8194:1;8190;8186:6;8182:14;8179:1;8176:21;8173:1;8169:29;8162:37;8159:1;8155:45;8145:172;;8234:10;8228:4;8221:24;8297:4;8291;8284:18;8145:172;-1:-1:-1;8340:9:0;;8336:17;;7895:476::o;170159:112::-;170215:14;170251:12;:5;170259:4;170251:12;:::i;14:286:1:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:1;;209:43;;199:71;;266:1;263;256:12;679:131;-1:-1:-1;;;;;754:31:1;;744:42;;734:70;;800:1;797;790:12;734:70;679:131;:::o;815:247::-;874:6;927:2;915:9;906:7;902:23;898:32;895:52;;;943:1;940;933:12;895:52;982:9;969:23;1001:31;1026:5;1001:31;:::i;1067:346::-;1135:6;1143;1196:2;1184:9;1175:7;1171:23;1167:32;1164:52;;;1212:1;1209;1202:12;1164:52;-1:-1:-1;;1257:23:1;;;1377:2;1362:18;;;1349:32;;-1:-1:-1;1067:346:1:o;1903:466::-;1980:6;1988;1996;2049:2;2037:9;2028:7;2024:23;2020:32;2017:52;;;2065:1;2062;2055:12;2017:52;-1:-1:-1;;2110:23:1;;;2230:2;2215:18;;2202:32;;-1:-1:-1;2333:2:1;2318:18;;;2305:32;;1903:466;-1:-1:-1;1903:466:1:o;2374:118::-;2460:5;2453:13;2446:21;2439:5;2436:32;2426:60;;2482:1;2479;2472:12;2497:965;2607:6;2615;2623;2631;2639;2647;2655;2708:3;2696:9;2687:7;2683:23;2679:33;2676:53;;;2725:1;2722;2715:12;2676:53;2770:23;;;-1:-1:-1;2890:2:1;2875:18;;2862:32;;-1:-1:-1;2993:2:1;2978:18;;2965:32;;-1:-1:-1;3096:2:1;3081:18;;3068:32;;-1:-1:-1;3199:3:1;3184:19;;3171:33;;-1:-1:-1;3303:3:1;3288:19;;3275:33;;-1:-1:-1;3386:3:1;3371:19;;3358:33;3400:30;3358:33;3400:30;:::i;:::-;3449:7;3439:17;;;2497:965;;;;;;;;;;:::o;5120:245::-;5187:6;5240:2;5228:9;5219:7;5215:23;5211:32;5208:52;;;5256:1;5253;5246:12;5208:52;5288:9;5282:16;5307:28;5329:5;5307:28;:::i;5370:270::-;5459:6;5512:2;5500:9;5491:7;5487:23;5483:32;5480:52;;;5528:1;5525;5518:12;5480:52;5560:9;5554:16;5579:31;5604:5;5579:31;:::i;5645:127::-;5706:10;5701:3;5697:20;5694:1;5687:31;5737:4;5734:1;5727:15;5761:4;5758:1;5751:15;5777:168;5850:9;;;5881;;5898:15;;;5892:22;;5878:37;5868:71;;5919:18;;:::i;5950:128::-;6017:9;;;6038:11;;;6035:37;;;6052:18;;:::i;6083:125::-;6148:9;;;6169:10;;;6166:36;;;6182:18;;:::i;6213:217::-;6253:1;6279;6269:132;;6323:10;6318:3;6314:20;6311:1;6304:31;6358:4;6355:1;6348:15;6386:4;6383:1;6376:15;6269:132;-1:-1:-1;6415:9:1;;6213:217::o

Swarm Source

ipfs://583f06c7154160a501f609d0b7eb9479b0ce8c373f842ddc46da976b2b5996dc

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
[ Download: CSV Export  ]

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