ETH Price: $3,502.20 (+2.50%)
Gas: 12 Gwei

Contract

0xb07ddF65B317da2137fE4485D40421229B5E5167
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202949952024-07-13 3:47:234 days ago1720842443
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931872024-07-12 21:43:114 days ago1720820591
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
202931842024-07-12 21:42:354 days ago1720820555
0xb07ddF65...29B5E5167
0 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
InterestRate

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 300 runs

Other Settings:
shanghai EvmVersion
File 1 of 4 : InterestRate.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { IYieldOracle } from "./interfaces/IYieldOracle.sol";
import { WadRayMath } from "./libraries/math/WadRayMath.sol";

// forgefmt: disable-start

struct IlkData {
    // Word 1
    uint96 adjustedProfitMargin; // 27 decimals
    uint96 minimumKinkRate; // 27 decimals

    // Word 2
    uint16 reserveFactor; // 4 decimals
    uint96 adjustedBaseRate; // 27 decimals
    uint96 minimumBaseRate; // 27 decimals
    uint16 optimalUtilizationRate; // 4 decimals
    uint16 distributionFactor; // 4 decimals

    // Word 3
    uint96 adjustedAboveKinkSlope; // 27 decimals
    uint96 minimumAboveKinkSlope; // 27 decimals
}

// Word 1
//
//                                                256  240   216   192                     96                      0
//                                                 |    |     |     |     min_kink_rate     |   adj_profit_margin  |
//
uint256 constant ADJUSTED_PROFIT_MARGIN_MASK =    0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; 
uint256 constant MINIMUM_KINK_RATE_MASK =         0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000;

// Word 2
//
//                                                256  240 224 208                     112                     16   0
//                                                 | __ |   |   |     min_base_rate     |     adj_base_rate     |   |
//                                                        ^   ^                                                   ^
//                                                        ^  opt_util                                 reserve_factor
//                                       distribution_factor

uint256 constant RESERVE_FACTOR_MASK =            0x000000000000000000000000000000000000000000000000000000000000FFFF;
uint256 constant ADJUSTED_BASE_RATE_MASK =        0x000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000;
uint256 constant MINIMUM_BASE_RATE_MASK =         0x000000000000FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000;
uint256 constant OPTIMAL_UTILIZATION_MASK =       0x00000000FFFF0000000000000000000000000000000000000000000000000000;
uint256 constant DISTRIBUTION_FACTOR_MASK =       0x0000FFFF00000000000000000000000000000000000000000000000000000000;

// Word 3
//                                                256  240   216   192                     96                      0
//                                                 |    |     |     |  min_above_kink_slope | adj_above_kink_slope |
//
uint256 constant ADJUSTED_ABOVE_KINK_SLOPE_MASK =  0x0000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF;
uint256 constant MINIMUM_ABOVE_KINK_SLOPE_MASK =   0x0000000000000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000; 

// forgefmt: disable-end

// Word 1
uint8 constant ADJUSTED_PROFIT_MARGIN_SHIFT = 0;
uint8 constant MINIMUM_KINK_RATE_SHIFT = 96;

// Word 2
uint8 constant RESERVE_FACTOR_SHIFT = 0;
uint8 constant ADJUSTED_BASE_RATE_SHIFT = 16;
uint8 constant MINIMUM_BASE_RATE_SHIFT = 16 + 96;
uint8 constant OPTIMAL_UTILIZATION_SHIFT = 16 + 96 + 96;
uint8 constant DISTRIBUTION_FACTOR_SHIFT = 16 + 96 + 96 + 16;

// Word 3
uint8 constant ADJUSTED_ABOVE_KINK_SLOPE_SHIFT = 0;
uint8 constant MINIMUM_ABOVE_KINK_SLOPE_SHIFT = 96;

uint48 constant SECONDS_IN_A_YEAR = 31_536_000;

/**
 * @notice An external contract that provides the APY for each collateral type.
 * A modular design here allows for updating of the parameters at a later date
 * without upgrading the core protocol.
 *
 * @dev Each collateral has its own interest rate model, and every operation on
 * the `IonPool` (lend, withdraw, borrow, repay) will alter the interest rate
 * for all collaterals. Therefore, before every operation, the previous interest
 * rate must be accrued. Ion determines the interest rate for each collateral
 * based on various collateral-specific parameters which must be stored
 * on-chain. However, to iterate through all these parameters as contract
 * storage on every operation introduces an immense gas overhead, especially as
 * more collaterals are listed on Ion. Therefore, this contract is heavily
 * optimized to reduce storage reads at the unfortunate cost of code-complexity.
 *
 * @custom:security-contact [email protected]
 */
contract InterestRate {
    using WadRayMath for *;

    error CollateralIndexOutOfBounds();
    error DistributionFactorsDoNotSumToOne(uint256 sum);
    error TotalDebtsLength(uint256 COLLATERAL_COUNT, uint256 totalIlkDebtsLength);

    error InvalidMinimumKinkRate(uint256 minimumKinkRate, uint256 minimumBaseRate);
    error InvalidIlkDataListLength(uint256 length);
    error InvalidOptimalUtilizationRate(uint256 optimalUtilizationRate);
    error InvalidReserveFactor(uint256 reserveFactor);
    error InvalidYieldOracleAddress();

    uint256 private constant MAX_ILKS = 8;

    /**
     * @dev Packed collateral configs
     */
    uint256 private immutable ILKCONFIG_0A;
    uint256 private immutable ILKCONFIG_0B;
    uint256 private immutable ILKCONFIG_0C;
    uint256 private immutable ILKCONFIG_1A;
    uint256 private immutable ILKCONFIG_1B;
    uint256 private immutable ILKCONFIG_1C;
    uint256 private immutable ILKCONFIG_2A;
    uint256 private immutable ILKCONFIG_2B;
    uint256 private immutable ILKCONFIG_2C;
    uint256 private immutable ILKCONFIG_3A;
    uint256 private immutable ILKCONFIG_3B;
    uint256 private immutable ILKCONFIG_3C;
    uint256 private immutable ILKCONFIG_4A;
    uint256 private immutable ILKCONFIG_4B;
    uint256 private immutable ILKCONFIG_4C;
    uint256 private immutable ILKCONFIG_5A;
    uint256 private immutable ILKCONFIG_5B;
    uint256 private immutable ILKCONFIG_5C;
    uint256 private immutable ILKCONFIG_6A;
    uint256 private immutable ILKCONFIG_6B;
    uint256 private immutable ILKCONFIG_6C;
    uint256 private immutable ILKCONFIG_7A;
    uint256 private immutable ILKCONFIG_7B;
    uint256 private immutable ILKCONFIG_7C;

    uint256 public immutable COLLATERAL_COUNT;
    IYieldOracle public immutable YIELD_ORACLE;

    /**
     * @notice Creates a new `InterestRate` instance.
     * @param ilkDataList List of ilk configs.
     * @param _yieldOracle Address of the Yield oracle.
     */
    constructor(IlkData[] memory ilkDataList, IYieldOracle _yieldOracle) {
        if (address(_yieldOracle) == address(0)) revert InvalidYieldOracleAddress();
        if (ilkDataList.length > MAX_ILKS) revert InvalidIlkDataListLength(ilkDataList.length);

        COLLATERAL_COUNT = ilkDataList.length;
        YIELD_ORACLE = _yieldOracle;

        uint256 distributionFactorSum = 0;
        for (uint256 i = 0; i < COLLATERAL_COUNT;) {
            distributionFactorSum += ilkDataList[i].distributionFactor;

            if (ilkDataList[i].minimumKinkRate < ilkDataList[i].minimumBaseRate) {
                revert InvalidMinimumKinkRate(ilkDataList[i].minimumKinkRate, ilkDataList[i].minimumBaseRate);
            }
            if (ilkDataList[i].optimalUtilizationRate == 0) {
                revert InvalidOptimalUtilizationRate(ilkDataList[i].optimalUtilizationRate);
            }
            if (ilkDataList[i].reserveFactor > 1e4) {
                revert InvalidReserveFactor(ilkDataList[i].reserveFactor);
            }

            // forgefmt: disable-next-line
            unchecked { ++i; }
        }

        if (distributionFactorSum != 1e4) revert DistributionFactorsDoNotSumToOne(distributionFactorSum);

        (ILKCONFIG_0A, ILKCONFIG_0B, ILKCONFIG_0C) = _packCollateralConfig(ilkDataList, 0);
        (ILKCONFIG_1A, ILKCONFIG_1B, ILKCONFIG_1C) = _packCollateralConfig(ilkDataList, 1);
        (ILKCONFIG_2A, ILKCONFIG_2B, ILKCONFIG_2C) = _packCollateralConfig(ilkDataList, 2);
        (ILKCONFIG_3A, ILKCONFIG_3B, ILKCONFIG_3C) = _packCollateralConfig(ilkDataList, 3);
        (ILKCONFIG_4A, ILKCONFIG_4B, ILKCONFIG_4C) = _packCollateralConfig(ilkDataList, 4);
        (ILKCONFIG_5A, ILKCONFIG_5B, ILKCONFIG_5C) = _packCollateralConfig(ilkDataList, 5);
        (ILKCONFIG_6A, ILKCONFIG_6B, ILKCONFIG_6C) = _packCollateralConfig(ilkDataList, 6);
        (ILKCONFIG_7A, ILKCONFIG_7B, ILKCONFIG_7C) = _packCollateralConfig(ilkDataList, 7);
    }

    /**
     * @notice Helper function to pack the collateral configs into 3 words. This
     * function is only called during construction.
     * @param ilkDataList The list of ilk configs.
     * @param index The ilkIndex to pack.
     * @return packedConfig_a
     * @return packedConfig_b
     * @return packedConfig_c
     */
    function _packCollateralConfig(
        IlkData[] memory ilkDataList,
        uint256 index
    )
        private
        view
        returns (uint256 packedConfig_a, uint256 packedConfig_b, uint256 packedConfig_c)
    {
        if (index >= COLLATERAL_COUNT) return (0, 0, 0);

        IlkData memory ilkData = ilkDataList[index];

        packedConfig_a = (
            uint256(ilkData.adjustedProfitMargin) << ADJUSTED_PROFIT_MARGIN_SHIFT
                | uint256(ilkData.minimumKinkRate) << MINIMUM_KINK_RATE_SHIFT
        );

        packedConfig_b = (
            uint256(ilkData.reserveFactor) << RESERVE_FACTOR_SHIFT
                | uint256(ilkData.adjustedBaseRate) << ADJUSTED_BASE_RATE_SHIFT
                | uint256(ilkData.minimumBaseRate) << MINIMUM_BASE_RATE_SHIFT
                | uint256(ilkData.optimalUtilizationRate) << OPTIMAL_UTILIZATION_SHIFT
                | uint256(ilkData.distributionFactor) << DISTRIBUTION_FACTOR_SHIFT
        );

        packedConfig_c = (
            uint256(ilkData.adjustedAboveKinkSlope) << ADJUSTED_ABOVE_KINK_SLOPE_SHIFT
                | uint256(ilkData.minimumAboveKinkSlope) << MINIMUM_ABOVE_KINK_SLOPE_SHIFT
        );
    }

    /**
     * @notice Helper function to unpack the collateral configs from the 3
     * words.
     * @param index The ilkIndex to unpack.
     * @return ilkData The unpacked collateral config.
     */
    function unpackCollateralConfig(uint256 index) external view returns (IlkData memory ilkData) {
        return _unpackCollateralConfig(index);
    }

    function _unpackCollateralConfig(uint256 index) internal view returns (IlkData memory ilkData) {
        if (index > COLLATERAL_COUNT - 1) revert CollateralIndexOutOfBounds();

        uint256 packedConfig_a;
        uint256 packedConfig_b;
        uint256 packedConfig_c;

        if (index == 0) {
            packedConfig_a = ILKCONFIG_0A;
            packedConfig_b = ILKCONFIG_0B;
            packedConfig_c = ILKCONFIG_0C;
        } else if (index == 1) {
            packedConfig_a = ILKCONFIG_1A;
            packedConfig_b = ILKCONFIG_1B;
            packedConfig_c = ILKCONFIG_1C;
        } else if (index == 2) {
            packedConfig_a = ILKCONFIG_2A;
            packedConfig_b = ILKCONFIG_2B;
            packedConfig_c = ILKCONFIG_2C;
        } else if (index == 3) {
            packedConfig_a = ILKCONFIG_3A;
            packedConfig_b = ILKCONFIG_3B;
            packedConfig_c = ILKCONFIG_3C;
        } else if (index == 4) {
            packedConfig_a = ILKCONFIG_4A;
            packedConfig_b = ILKCONFIG_4B;
            packedConfig_c = ILKCONFIG_4C;
        } else if (index == 5) {
            packedConfig_a = ILKCONFIG_5A;
            packedConfig_b = ILKCONFIG_5B;
            packedConfig_c = ILKCONFIG_5C;
        } else if (index == 6) {
            packedConfig_a = ILKCONFIG_6A;
            packedConfig_b = ILKCONFIG_6B;
            packedConfig_c = ILKCONFIG_6C;
        } else if (index == 7) {
            packedConfig_a = ILKCONFIG_7A;
            packedConfig_b = ILKCONFIG_7B;
            packedConfig_c = ILKCONFIG_7C;
        }

        uint96 adjustedProfitMargin =
            uint96((packedConfig_a & ADJUSTED_PROFIT_MARGIN_MASK) >> ADJUSTED_PROFIT_MARGIN_SHIFT);
        uint96 minimumKinkRate = uint96((packedConfig_a & MINIMUM_KINK_RATE_MASK) >> MINIMUM_KINK_RATE_SHIFT);

        uint16 reserveFactor = uint16((packedConfig_b & RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_SHIFT);
        uint96 adjustedBaseRate = uint96((packedConfig_b & ADJUSTED_BASE_RATE_MASK) >> ADJUSTED_BASE_RATE_SHIFT);
        uint96 minimumBaseRate = uint96((packedConfig_b & MINIMUM_BASE_RATE_MASK) >> MINIMUM_BASE_RATE_SHIFT);
        uint16 optimalUtilizationRate = uint16((packedConfig_b & OPTIMAL_UTILIZATION_MASK) >> OPTIMAL_UTILIZATION_SHIFT);
        uint16 distributionFactor = uint16((packedConfig_b & DISTRIBUTION_FACTOR_MASK) >> DISTRIBUTION_FACTOR_SHIFT);

        uint96 adjustedAboveKinkSlope =
            uint96((packedConfig_c & ADJUSTED_ABOVE_KINK_SLOPE_MASK) >> ADJUSTED_ABOVE_KINK_SLOPE_SHIFT);
        uint96 minimumAboveKinkSlope =
            uint96((packedConfig_c & MINIMUM_ABOVE_KINK_SLOPE_MASK) >> MINIMUM_ABOVE_KINK_SLOPE_SHIFT);

        ilkData = IlkData({
            adjustedProfitMargin: adjustedProfitMargin,
            minimumKinkRate: minimumKinkRate,
            reserveFactor: reserveFactor,
            adjustedBaseRate: adjustedBaseRate,
            minimumBaseRate: minimumBaseRate,
            optimalUtilizationRate: optimalUtilizationRate,
            distributionFactor: distributionFactor,
            adjustedAboveKinkSlope: adjustedAboveKinkSlope,
            minimumAboveKinkSlope: minimumAboveKinkSlope
        });
    }

    /**
     * @notice Calculates the interest rate for a given collateral.
     * @param ilkIndex Index of the collateral.
     * @param totalIlkDebt Total debt of the collateral. [RAD]
     * @param totalEthSupply Total eth supply of the system. [WAD]
     * @return The borrow rate for the collateral. [RAY]
     * @return The reserve factor for the collateral. [RAY]
     */
    function calculateInterestRate(
        uint256 ilkIndex,
        uint256 totalIlkDebt,
        uint256 totalEthSupply
    )
        external
        view
        returns (uint256, uint256)
    {
        IlkData memory ilkData = _unpackCollateralConfig(ilkIndex);
        uint256 optimalUtilizationRateRay = ilkData.optimalUtilizationRate.scaleUpToRay(4);
        uint256 collateralApyRayInSeconds = YIELD_ORACLE.apys(ilkIndex).scaleUpToRay(8) / SECONDS_IN_A_YEAR;

        uint256 distributionFactor = ilkData.distributionFactor;
        // The only time the distribution factor will be set to 0 is when a
        // market has been sunset. In this case, we want to prevent division by
        // 0, but we also want to prevent the borrow rate from skyrocketing. So
        // we will return a reasonable borrow rate of kink utilization on the
        // minimum curve.
        if (distributionFactor == 0) {
            return (ilkData.minimumKinkRate, ilkData.reserveFactor.scaleUpToRay(4));
        }
        // [RAD] / [WAD] = [RAY]
        uint256 utilizationRate =
            totalEthSupply == 0 ? 0 : totalIlkDebt / (totalEthSupply.wadMulDown(distributionFactor.scaleUpToWad(4)));

        // Avoid stack too deep
        uint256 adjustedBelowKinkSlope;
        {
            uint256 slopeNumerator;
            unchecked {
                slopeNumerator = collateralApyRayInSeconds - ilkData.adjustedProfitMargin - ilkData.adjustedBaseRate;
            }

            // Underflow occurred
            // If underflow occurred, then the Apy was too low or the profitMargin was too high and
            // we would want to switch to minimum borrow rate. Set slopeNumerator to zero such
            // that adjusted borrow rate is below the minimum borrow rate.
            if (slopeNumerator > collateralApyRayInSeconds) {
                slopeNumerator = 0;
            }

            adjustedBelowKinkSlope = slopeNumerator.rayDivDown(optimalUtilizationRateRay);
        }

        uint256 minimumBelowKinkSlope =
            (ilkData.minimumKinkRate - ilkData.minimumBaseRate).rayDivDown(optimalUtilizationRateRay);

        // Below kink
        if (utilizationRate < optimalUtilizationRateRay) {
            uint256 adjustedBorrowRate = adjustedBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.adjustedBaseRate;
            uint256 minimumBorrowRate = minimumBelowKinkSlope.rayMulDown(utilizationRate) + ilkData.minimumBaseRate;

            if (adjustedBorrowRate < minimumBorrowRate) {
                return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
            } else {
                return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
            }
        }
        // Above kink
        else {
            // For the above kink calculation, we will use the below kink slope
            // for all utilization up until the kink. From that point on we will
            // use the above kink slope.
            uint256 excessUtil = utilizationRate - optimalUtilizationRateRay;

            uint256 adjustedNormalRate =
                adjustedBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.adjustedBaseRate;
            uint256 minimumNormalRate =
                minimumBelowKinkSlope.rayMulDown(optimalUtilizationRateRay) + ilkData.minimumBaseRate;

            // [WAD] * [RAY] / [WAD] = [RAY]
            uint256 adjustedBorrowRate = ilkData.adjustedAboveKinkSlope.rayMulDown(excessUtil) + adjustedNormalRate;
            uint256 minimumBorrowRate = ilkData.minimumAboveKinkSlope.rayMulDown(excessUtil) + minimumNormalRate;

            if (adjustedBorrowRate < minimumBorrowRate) {
                return (minimumBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
            } else {
                return (adjustedBorrowRate, ilkData.reserveFactor.scaleUpToRay(4));
            }
        }
    }
}

File 2 of 4 : IYieldOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IYieldOracle {
    function apys(uint256 ilkIndex) external view returns (uint32);
}

File 3 of 4 : WadRayMath.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";

uint256 constant WAD = 1e18;
uint256 constant RAY = 1e27;
uint256 constant RAD = 1e45;

/**
 * @title WadRayMath
 *
 * @notice This library provides mul/div[up/down] functionality for WAD, RAY and
 * RAD with phantom overflow protection as well as scale[up/down] functionality
 * for WAD, RAY and RAD.
 *
 * @custom:security-contact [email protected]
 */
library WadRayMath {
    using Math for uint256;

    error NotScalingUp(uint256 from, uint256 to);
    error NotScalingDown(uint256 from, uint256 to);

    /**
     * @notice Multiplies two WAD numbers and returns the result as a WAD
     * rounding the result down.
     * @param a Multiplicand.
     * @param b Multiplier.
     */
    function wadMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, WAD);
    }

    /**
     * @notice Multiplies two WAD numbers and returns the result as a WAD
     * rounding the result up.
     * @param a Multiplicand.
     * @param b Multiplier.
     */
    function wadMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, WAD, Math.Rounding.Ceil);
    }

    /**
     * @notice Divides two WAD numbers and returns the result as a WAD rounding
     * the result down.
     * @param a Dividend.
     * @param b Divisor.
     */
    function wadDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(WAD, b);
    }

    /**
     * @notice Divides two WAD numbers and returns the result as a WAD rounding
     * the result up.
     * @param a Dividend.
     * @param b Divisor.
     */
    function wadDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(WAD, b, Math.Rounding.Ceil);
    }

    /**
     * @notice Multiplies two RAY numbers and returns the result as a RAY
     * rounding the result down.
     * @param a Multiplicand
     * @param b Multiplier
     */
    function rayMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, RAY);
    }

    /**
     * @notice Multiplies two RAY numbers and returns the result as a RAY
     * rounding the result up.
     * @param a Multiplicand
     * @param b Multiplier
     */
    function rayMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, RAY, Math.Rounding.Ceil);
    }

    /**
     * @notice Divides two RAY numbers and returns the result as a RAY
     * rounding the result down.
     * @param a Dividend
     * @param b Divisor
     */
    function rayDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(RAY, b);
    }

    /**
     * @notice Divides two RAY numbers and returns the result as a RAY
     * rounding the result up.
     * @param a Dividend
     * @param b Divisor
     */
    function rayDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(RAY, b, Math.Rounding.Ceil);
    }

    /**
     * @notice Multiplies two RAD numbers and returns the result as a RAD
     * rounding the result down.
     * @param a Multiplicand
     * @param b Multiplier
     */
    function radMulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, RAD);
    }

    /**
     * @notice Multiplies two RAD numbers and returns the result as a RAD
     * rounding the result up.
     * @param a Multiplicand
     * @param b Multiplier
     */
    function radMulUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(b, RAD, Math.Rounding.Ceil);
    }

    /**
     * @notice Divides two RAD numbers and returns the result as a RAD rounding
     * the result down.
     * @param a Dividend
     * @param b Divisor
     */
    function radDivDown(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(RAD, b);
    }

    /**
     * @notice Divides two RAD numbers and returns the result as a RAD rounding
     * the result up.
     * @param a Dividend
     * @param b Divisor
     */
    function radDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mulDiv(RAD, b, Math.Rounding.Ceil);
    }

    // --- Scalers ---

    /**
     * @notice Scales a value up from WAD. NOTE: The `scale` value must be
     * less than 18.
     * @param value to scale up.
     * @param scale of the returned value.
     */
    function scaleUpToWad(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleUp(value, scale, 18);
    }

    /**
     * @notice Scales a value up from RAY. NOTE: The `scale` value must be
     * less than 27.
     * @param value to scale up.
     * @param scale of the returned value.
     */
    function scaleUpToRay(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleUp(value, scale, 27);
    }

    /**
     * @notice Scales a value up from RAD. NOTE: The `scale` value must be
     * less than 45.
     * @param value to scale up.
     * @param scale of the returned value.
     */
    function scaleUpToRad(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleUp(value, scale, 45);
    }

    /**
     * @notice Scales a value down to WAD. NOTE: The `scale` value must be
     * greater than 18.
     * @param value to scale down.
     * @param scale of the returned value.
     */
    function scaleDownToWad(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleDown(value, scale, 18);
    }

    /**
     * @notice Scales a value down to RAY. NOTE: The `scale` value must be
     * greater than 27.
     * @param value to scale down.
     * @param scale of the returned value.
     */
    function scaleDownToRay(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleDown(value, scale, 27);
    }

    /**
     * @notice Scales a value down to RAD. NOTE: The `scale` value must be
     * greater than 45.
     * @param value to scale down.
     * @param scale of the returned value.
     */
    function scaleDownToRad(uint256 value, uint256 scale) internal pure returns (uint256) {
        return scaleDown(value, scale, 45);
    }

    /**
     * @notice Scales a value up from one fixed-point precision to another.
     * @param value to scale up.
     * @param from Precision to scale from.
     * @param to Precision to scale to.
     */
    function scaleUp(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {
        if (from >= to) revert NotScalingUp(from, to);
        return value * (10 ** (to - from));
    }

    /**
     * @notice Scales a value down from one fixed-point precision to another.
     * @param value to scale down.
     * @param from Precision to scale from.
     * @param to Precision to scale to.
     */
    function scaleDown(uint256 value, uint256 from, uint256 to) internal pure returns (uint256) {
        if (from <= to) revert NotScalingDown(from, to);
        return value / (10 ** (from - to));
    }
}

File 4 of 4 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

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

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

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the 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.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@balancer-labs/v2-interfaces/=lib/balancer-v2-monorepo/pkg/interfaces/",
    "@balancer-labs/v2-pool-stable/=lib/balancer-v2-monorepo/pkg/pool-stable/",
    "@chainlink/contracts/=lib/chainlink/contracts/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "balancer-v2-monorepo/=lib/balancer-v2-monorepo/",
    "chainlink/=lib/chainlink/",
    "ds-test/=lib/forge-safe/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-safe/=lib/forge-safe/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solady/=lib/solady/",
    "solidity-stringutils/=lib/forge-safe/lib/surl/lib/solidity-stringutils/",
    "solmate/=lib/forge-safe/lib/solmate/src/",
    "surl/=lib/forge-safe/lib/surl/",
    "v3-core/=lib/v3-core/",
    "v3-periphery/=lib/v3-periphery/contracts/",
    "solarray/=lib/solarray/src/",
    "pendle-core-v2-public/=lib/pendle-core-v2-public/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 300
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"uint96","name":"adjustedProfitMargin","type":"uint96"},{"internalType":"uint96","name":"minimumKinkRate","type":"uint96"},{"internalType":"uint16","name":"reserveFactor","type":"uint16"},{"internalType":"uint96","name":"adjustedBaseRate","type":"uint96"},{"internalType":"uint96","name":"minimumBaseRate","type":"uint96"},{"internalType":"uint16","name":"optimalUtilizationRate","type":"uint16"},{"internalType":"uint16","name":"distributionFactor","type":"uint16"},{"internalType":"uint96","name":"adjustedAboveKinkSlope","type":"uint96"},{"internalType":"uint96","name":"minimumAboveKinkSlope","type":"uint96"}],"internalType":"struct IlkData[]","name":"ilkDataList","type":"tuple[]"},{"internalType":"contract IYieldOracle","name":"_yieldOracle","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CollateralIndexOutOfBounds","type":"error"},{"inputs":[{"internalType":"uint256","name":"sum","type":"uint256"}],"name":"DistributionFactorsDoNotSumToOne","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"InvalidIlkDataListLength","type":"error"},{"inputs":[{"internalType":"uint256","name":"minimumKinkRate","type":"uint256"},{"internalType":"uint256","name":"minimumBaseRate","type":"uint256"}],"name":"InvalidMinimumKinkRate","type":"error"},{"inputs":[{"internalType":"uint256","name":"optimalUtilizationRate","type":"uint256"}],"name":"InvalidOptimalUtilizationRate","type":"error"},{"inputs":[{"internalType":"uint256","name":"reserveFactor","type":"uint256"}],"name":"InvalidReserveFactor","type":"error"},{"inputs":[],"name":"InvalidYieldOracleAddress","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"to","type":"uint256"}],"name":"NotScalingUp","type":"error"},{"inputs":[{"internalType":"uint256","name":"COLLATERAL_COUNT","type":"uint256"},{"internalType":"uint256","name":"totalIlkDebtsLength","type":"uint256"}],"name":"TotalDebtsLength","type":"error"},{"inputs":[],"name":"COLLATERAL_COUNT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"YIELD_ORACLE","outputs":[{"internalType":"contract IYieldOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ilkIndex","type":"uint256"},{"internalType":"uint256","name":"totalIlkDebt","type":"uint256"},{"internalType":"uint256","name":"totalEthSupply","type":"uint256"}],"name":"calculateInterestRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"unpackCollateralConfig","outputs":[{"components":[{"internalType":"uint96","name":"adjustedProfitMargin","type":"uint96"},{"internalType":"uint96","name":"minimumKinkRate","type":"uint96"},{"internalType":"uint16","name":"reserveFactor","type":"uint16"},{"internalType":"uint96","name":"adjustedBaseRate","type":"uint96"},{"internalType":"uint96","name":"minimumBaseRate","type":"uint96"},{"internalType":"uint16","name":"optimalUtilizationRate","type":"uint16"},{"internalType":"uint16","name":"distributionFactor","type":"uint16"},{"internalType":"uint96","name":"adjustedAboveKinkSlope","type":"uint96"},{"internalType":"uint96","name":"minimumAboveKinkSlope","type":"uint96"}],"internalType":"struct IlkData","name":"ilkData","type":"tuple"}],"stateMutability":"view","type":"function"}]

6103c060405234801562000011575f80fd5b50604051620016123803806200161283398101604081905262000034916200054d565b6001600160a01b0381166200005c57604051633a9f6af760e11b815260040160405180910390fd5b6008825111156200008f578151604051637792a1c560e11b81526004016200008691815260200190565b60405180910390fd5b8151610380526001600160a01b0381166103a0525f805b61038051811015620002a457838181518110620000c757620000c7620006ca565b602002602001015160c0015161ffff1682620000e49190620006de565b9150838181518110620000fb57620000fb620006ca565b6020026020010151608001516001600160601b0316848281518110620001255762000125620006ca565b6020026020010151602001516001600160601b03161015620001b457838181518110620001565762000156620006ca565b602002602001015160200151848281518110620001775762000177620006ca565b602002602001015160800151604051633f6ca22160e11b8152600401620000869291906001600160601b0392831681529116602082015260400190565b838181518110620001c957620001c9620006ca565b602002602001015160a0015161ffff165f036200022657838181518110620001f557620001f5620006ca565b602002602001015160a0015160405163f2ae6b3d60e01b815260040162000086919061ffff91909116815260200190565b6127108482815181106200023e576200023e620006ca565b60200260200101516040015161ffff1611156200029b578381815181106200026a576200026a620006ca565b602002602001015160400151604051630cd3498760e11b815260040162000086919061ffff91909116815260200190565b600101620000a6565b508061271014620002cc5760405163b9778c0960e01b81526004810182905260240162000086565b620002d8835f62000399565b60c05260a052608052620002ee83600162000399565b610120526101005260e0526200030683600262000399565b6101805261016052610140526200031f83600362000399565b6101e0526101c0526101a0526200033883600462000399565b6102405261022052610200526200035183600562000399565b6102a05261028052610260526200036a83600662000399565b610300526102e0526102c0526200038383600762000399565b6103605261034052610320525062000704915050565b5f805f610380518410620003b557505f9150819050806200048e565b5f858581518110620003cb57620003cb620006ca565b60200260200101519050606060ff1681602001516001600160601b0316901b5f60ff16825f01516001600160601b0316901b17935060e060ff168160c0015161ffff16901b60d060ff168260a0015161ffff16901b607060ff1683608001516001600160601b0316901b601060ff1684606001516001600160601b0316901b5f60ff16856040015161ffff16901b171717179250606060ff168161010001516001600160601b0316901b5f60ff168260e001516001600160601b0316901b179150505b9250925092565b634e487b7160e01b5f52604160045260245ffd5b60405161012081016001600160401b0381118282101715620004cf57620004cf62000495565b60405290565b604051601f8201601f191681016001600160401b038111828210171562000500576200050062000495565b604052919050565b80516001600160601b03811681146200051f575f80fd5b919050565b805161ffff811681146200051f575f80fd5b80516001600160a01b03811681146200051f575f80fd5b5f80604080848603121562000560575f80fd5b83516001600160401b038082111562000577575f80fd5b818601915086601f8301126200058b575f80fd5b8151602082821115620005a257620005a262000495565b620005b2818360051b01620004d5565b828152818101935061012092830285018201928a841115620005d2575f80fd5b948201945b83861015620006ac5780868c031215620005f0575f8081fd5b620005fa620004a9565b620006058762000508565b81526200061484880162000508565b848201526200062588880162000524565b8882015260606200063881890162000508565b9082015260806200064b88820162000508565b9082015260a06200065e88820162000524565b9082015260c06200067188820162000524565b9082015260e06200068488820162000508565b908201526101006200069888820162000508565b9082015285529485019493820193620005d7565b509650620006bc88820162000536565b955050505050509250929050565b634e487b7160e01b5f52603260045260245ffd5b80820180821115620006fe57634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161036051610380516103a051610df2620008205f395f8181605301526101a801525f818160b7015261051501525f6108df01525f6108bc01525f61089901525f61086a01525f61084701525f61082401525f6107f501525f6107d201525f6107af01525f61078001525f61075d01525f61073a01525f61070b01525f6106e801525f6106c501525f61069601525f61067301525f61065001525f61062101525f6105fe01525f6105db01525f6105ac01525f61058901525f6105660152610df25ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c806309ea519f1461004e578063161fa6561461009257806348d4b487146100b2578063fe4bab43146100e7575b5f80fd5b6100757f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100a56100a0366004610af5565b61010f565b6040516100899190610b0c565b6100d97f000000000000000000000000000000000000000000000000000000000000000081565b604051908152602001610089565b6100fa6100f5366004610bda565b610166565b60408051928352602083019190915201610089565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152610160826104c6565b92915050565b5f805f610172866104c6565b90505f61019160048360a0015161ffff1661097b90919063ffffffff16565b90505f6301e1338065ffffffffffff1661024760087f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166381ce1c238c6040518263ffffffff1660e01b81526004016101f491815260200190565b602060405180830381865afa15801561020f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102339190610c03565b63ffffffff1661097b90919063ffffffff16565b6102519190610c4e565b60c084015190915061ffff165f819003610297576020840151604085015161027e9061ffff16600461097b565b816001600160601b0316915095509550505050506104be565b5f87156102c2576102b36102ac83600461098f565b899061099c565b6102bd908a610c4e565b6102c4565b5f5b606086015186519192505f916001600160601b039182169116850303848111156102eb57505f5b6102f581876109b0565b9150505f61032086886080015189602001516103119190610c6d565b6001600160601b0316906109b0565b9050858310156103bd5760608701515f906001600160601b031661034484866109c8565b61034e9190610c94565b60808901519091505f906001600160601b031661036b84876109c8565b6103759190610c94565b9050808210156103a757604089015181906103959061ffff16600461097b565b9a509a505050505050505050506104be565b604089015182906103959061ffff16600461097b565b5f6103c88785610ca7565b60608901519091505f906001600160601b03166103e5858a6109c8565b6103ef9190610c94565b60808a01519091505f906001600160601b031661040c858b6109c8565b6104169190610c94565b90505f8261043a858d60e001516001600160601b03166109c890919063ffffffff16565b6104449190610c94565b90505f82610469868e61010001516001600160601b03166109c890919063ffffffff16565b6104739190610c94565b9050808210156104a85760408c015181906104939061ffff16600461097b565b9d509d505050505050505050505050506104be565b60408c015182906104939061ffff16600461097b565b935093915050565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915261053960017f0000000000000000000000000000000000000000000000000000000000000000610ca7565b8211156105595760405163ca89fc4960e01b815260040160405180910390fd5b5f805f845f036105d057507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460010361064557507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846002036106ba57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460030361072f57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846004036107a457507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460050361081957507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460060361088e57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846007036108ff57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000005b60408051610120810182526001600160601b038581168252606095861c8116602083015261ffff85811693830193909352601085901c811682870152607085901c8116608083015260d085901c831660a083015260e094851c90921660c082015282821693810193909352921c90911661010082015292915050565b5f6109888383601b6109e0565b9392505050565b5f610988838360126109e0565b5f6109888383670de0b6b3a7640000610a36565b5f610988836b033b2e3c9fd0803ce800000084610a36565b5f61098883836b033b2e3c9fd0803ce8000000610a36565b5f818310610a0f57604051631a065cf160e01b8152600481018490526024810183905260440160405180910390fd5b610a198383610ca7565b610a2490600a610d9a565b610a2e9085610da5565b949350505050565b5f838302815f1985870982811083820303915050805f03610a6a57838281610a6057610a60610c26565b0492505050610988565b808411610a8a5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60208284031215610b05575f80fd5b5035919050565b5f610120820190506001600160601b03808451168352806020850151166020840152506040830151610b44604084018261ffff169052565b506060830151610b5f60608401826001600160601b03169052565b506080830151610b7a60808401826001600160601b03169052565b5060a0830151610b9060a084018261ffff169052565b5060c0830151610ba660c084018261ffff169052565b5060e0830151610bc160e08401826001600160601b03169052565b50610100928301516001600160601b0316919092015290565b5f805f60608486031215610bec575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215610c13575f80fd5b815163ffffffff81168114610988575f80fd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f82610c6857634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160601b03828116828216039080821115610c8d57610c8d610c3a565b5092915050565b8082018082111561016057610160610c3a565b8181038181111561016057610160610c3a565b600181815b80851115610cf457815f1904821115610cda57610cda610c3a565b80851615610ce757918102915b93841c9390800290610cbf565b509250929050565b5f82610d0a57506001610160565b81610d1657505f610160565b8160018114610d2c5760028114610d3657610d52565b6001915050610160565b60ff841115610d4757610d47610c3a565b50506001821b610160565b5060208310610133831016604e8410600b8410161715610d75575081810a610160565b610d7f8383610cba565b805f1904821115610d9257610d92610c3a565b029392505050565b5f6109888383610cfc565b808202811582820484141761016057610160610c3a56fea2646970667358221220b2f7cb8e5fce56ccf7c3e226d1c8b8bfa819b4b0fee4fd484311316a2df06e7664736f6c634300081500330000000000000000000000000000000000000000000000000000000000000040000000000000000000000000437cc840e234c2127f54cd59b0b18af59c586760000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a0ff73f71548d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf033b95a04e000000000000000000000000000000000000000000000000000000000000000232800000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fa5fa5ef28e4c800

Deployed Bytecode

0x608060405234801561000f575f80fd5b506004361061004a575f3560e01c806309ea519f1461004e578063161fa6561461009257806348d4b487146100b2578063fe4bab43146100e7575b5f80fd5b6100757f000000000000000000000000437cc840e234c2127f54cd59b0b18af59c58676081565b6040516001600160a01b0390911681526020015b60405180910390f35b6100a56100a0366004610af5565b61010f565b6040516100899190610b0c565b6100d97f000000000000000000000000000000000000000000000000000000000000000181565b604051908152602001610089565b6100fa6100f5366004610bda565b610166565b60408051928352602083019190915201610089565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810191909152610160826104c6565b92915050565b5f805f610172866104c6565b90505f61019160048360a0015161ffff1661097b90919063ffffffff16565b90505f6301e1338065ffffffffffff1661024760087f000000000000000000000000437cc840e234c2127f54cd59b0b18af59c5867606001600160a01b03166381ce1c238c6040518263ffffffff1660e01b81526004016101f491815260200190565b602060405180830381865afa15801561020f573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102339190610c03565b63ffffffff1661097b90919063ffffffff16565b6102519190610c4e565b60c084015190915061ffff165f819003610297576020840151604085015161027e9061ffff16600461097b565b816001600160601b0316915095509550505050506104be565b5f87156102c2576102b36102ac83600461098f565b899061099c565b6102bd908a610c4e565b6102c4565b5f5b606086015186519192505f916001600160601b039182169116850303848111156102eb57505f5b6102f581876109b0565b9150505f61032086886080015189602001516103119190610c6d565b6001600160601b0316906109b0565b9050858310156103bd5760608701515f906001600160601b031661034484866109c8565b61034e9190610c94565b60808901519091505f906001600160601b031661036b84876109c8565b6103759190610c94565b9050808210156103a757604089015181906103959061ffff16600461097b565b9a509a505050505050505050506104be565b604089015182906103959061ffff16600461097b565b5f6103c88785610ca7565b60608901519091505f906001600160601b03166103e5858a6109c8565b6103ef9190610c94565b60808a01519091505f906001600160601b031661040c858b6109c8565b6104169190610c94565b90505f8261043a858d60e001516001600160601b03166109c890919063ffffffff16565b6104449190610c94565b90505f82610469868e61010001516001600160601b03166109c890919063ffffffff16565b6104739190610c94565b9050808210156104a85760408c015181906104939061ffff16600461097b565b9d509d505050505050505050505050506104be565b60408c015182906104939061ffff16600461097b565b935093915050565b60408051610120810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915261053960017f0000000000000000000000000000000000000000000000000000000000000001610ca7565b8211156105595760405163ca89fc4960e01b815260040160405180910390fd5b5f805f845f036105d057507f0000000000000000000000002a0ff73f71548d0000000000000000000000000091507f000027102328000000000bf033b95a04e000000000000000000000000000000090507f000000000000000000000003fa5fa5ef28e4c8000000000000000000000000006108ff565b8460010361064557507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846002036106ba57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460030361072f57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846004036107a457507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460050361081957507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b8460060361088e57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000006108ff565b846007036108ff57507f000000000000000000000000000000000000000000000000000000000000000091507f000000000000000000000000000000000000000000000000000000000000000090507f00000000000000000000000000000000000000000000000000000000000000005b60408051610120810182526001600160601b038581168252606095861c8116602083015261ffff85811693830193909352601085901c811682870152607085901c8116608083015260d085901c831660a083015260e094851c90921660c082015282821693810193909352921c90911661010082015292915050565b5f6109888383601b6109e0565b9392505050565b5f610988838360126109e0565b5f6109888383670de0b6b3a7640000610a36565b5f610988836b033b2e3c9fd0803ce800000084610a36565b5f61098883836b033b2e3c9fd0803ce8000000610a36565b5f818310610a0f57604051631a065cf160e01b8152600481018490526024810183905260440160405180910390fd5b610a198383610ca7565b610a2490600a610d9a565b610a2e9085610da5565b949350505050565b5f838302815f1985870982811083820303915050805f03610a6a57838281610a6057610a60610c26565b0492505050610988565b808411610a8a5760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b5f60208284031215610b05575f80fd5b5035919050565b5f610120820190506001600160601b03808451168352806020850151166020840152506040830151610b44604084018261ffff169052565b506060830151610b5f60608401826001600160601b03169052565b506080830151610b7a60808401826001600160601b03169052565b5060a0830151610b9060a084018261ffff169052565b5060c0830151610ba660c084018261ffff169052565b5060e0830151610bc160e08401826001600160601b03169052565b50610100928301516001600160601b0316919092015290565b5f805f60608486031215610bec575f80fd5b505081359360208301359350604090920135919050565b5f60208284031215610c13575f80fd5b815163ffffffff81168114610988575f80fd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f82610c6857634e487b7160e01b5f52601260045260245ffd5b500490565b6001600160601b03828116828216039080821115610c8d57610c8d610c3a565b5092915050565b8082018082111561016057610160610c3a565b8181038181111561016057610160610c3a565b600181815b80851115610cf457815f1904821115610cda57610cda610c3a565b80851615610ce757918102915b93841c9390800290610cbf565b509250929050565b5f82610d0a57506001610160565b81610d1657505f610160565b8160018114610d2c5760028114610d3657610d52565b6001915050610160565b60ff841115610d4757610d47610c3a565b50506001821b610160565b5060208310610133831016604e8410600b8410161715610d75575081810a610160565b610d7f8383610cba565b805f1904821115610d9257610d92610c3a565b029392505050565b5f6109888383610cfc565b808202811582820484141761016057610160610c3a56fea2646970667358221220b2f7cb8e5fce56ccf7c3e226d1c8b8bfa819b4b0fee4fd484311316a2df06e7664736f6c63430008150033

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

0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000437cc840e234c2127f54cd59b0b18af59c586760000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a0ff73f71548d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf033b95a04e000000000000000000000000000000000000000000000000000000000000000232800000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fa5fa5ef28e4c800

-----Decoded View---------------
Arg [0] : ilkDataList (tuple[]): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
Arg [1] : _yieldOracle (address): 0x437CC840e234C2127f54CD59B0B18aF59c586760

-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 000000000000000000000000437cc840e234c2127f54cd59b0b18af59c586760
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000002a0ff73f71548d00
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000bf033b95a04e000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000002328
Arg [9] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 000000000000000000000000000000000000000000000003fa5fa5ef28e4c800


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.