IRMTwyneCurve

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/twyne/IRMTwyneCurve.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.28;

import {IIRM} from "euler-vault-kit/InterestRateModels/IIRM.sol";
import {SECONDS_PER_YEAR} from "euler-vault-kit/EVault/shared/Constants.sol";

/// @title IRMLinearKink
/// @notice To contact the team regarding security matters, visit https://twyne.xyz/security
/// @notice Implementation of a curved interest rate model
/// @notice interest rate grows linearly with utilization and increases faster after reaching a kink
contract IRMTwyneCurve is IIRM {
    /// @notice Approx interest rate at linearKinkUtilizationRate
    uint public immutable idealKinkInterestRate; // 0.6e4 = 60%
    /// @notice Utilization rate value used in calculating IRM parameters
    /// @notice beyond this utilization rate, the polynomial term dominates (this value must be greater than nonlinearPoint)
    /// @notice while this is a utilization rate that would have 1e18 decimals, it is ONLY used to derive curve params so use 1e4
    uint public immutable linearKinkUtilizationRate; // 0.8e4 = 80%
    /// @notice Max interest rate applied at 100% utilization
    uint public immutable maxInterestRate; // 5e4 = 500%

    /// @notice When utilization rate is less than nonlinearPoint, assume linear model (polynomial term is not significant)
    uint public immutable nonlinearPoint; // 5e17 = 50%

    /// @notice Curve parameters
    uint public immutable linearParameter;
    uint public immutable polynomialParameter;

    /// @notice LTV values have 1e4 decimals
    uint internal constant MAXFACTOR = 1e4;

    constructor(uint16 idealKinkInterestRate_, uint16 linearKinkUtilizationRate_, uint16 maxInterestRate_, uint nonlinearPoint_) {
        if (
            idealKinkInterestRate_ > maxInterestRate_
            || linearKinkUtilizationRate_ > MAXFACTOR
            // || idealKinkInterestRate_ == 0
            || linearKinkUtilizationRate_ == 0
            || maxInterestRate_ == 0
            || nonlinearPoint_ == 0
            || nonlinearPoint_ >= 1e14 * uint(linearKinkUtilizationRate_) // 1e14 is to align decimals
        ) revert E_IRMUpdateUnauthorized();

        idealKinkInterestRate = idealKinkInterestRate_;
        linearKinkUtilizationRate = linearKinkUtilizationRate_;
        maxInterestRate = maxInterestRate_;
        nonlinearPoint = nonlinearPoint_;
        linearParameter = idealKinkInterestRate * MAXFACTOR / linearKinkUtilizationRate_;
        polynomialParameter = maxInterestRate_ - linearParameter;
    }

    /// @inheritdoc IIRM
    function computeInterestRate(address vault, uint cash, uint borrows)
        external
        view
        override
        returns (uint)
    {
        if (msg.sender != vault) revert E_IRMUpdateUnauthorized();

        return computeInterestRateInternal(vault, cash, borrows);
    }

    /// @inheritdoc IIRM
    function computeInterestRateView(address vault, uint cash, uint borrows)
        external
        view
        override
        returns (uint)
    {
        return computeInterestRateInternal(vault, cash, borrows);
    }

    function computeInterestRateInternal(address, uint cash, uint borrows) internal view returns (uint ir) {
        uint totalAssets = cash + borrows;

        // utilization has decimals of 1e18, where 1e18 = 100%
        uint utilization = totalAssets == 0
            ? 0 // prevent divide by zero case by returning zero
            : borrows * 1e18 / totalAssets;

        ir = linearParameter * utilization;

        if (utilization > nonlinearPoint) {
            // Use gamma of 12
            // utilization^2
            uint utilTemp4 = (utilization * utilization) / 1e18;
            // utilization^4
            utilTemp4 = (utilTemp4 * utilTemp4) / 1e18;
            // utilization^8
            uint utilpow = (utilTemp4 * utilTemp4) / 1e18;
            // utilization^12
            utilpow = (utilpow * utilTemp4) / 1e18;

            ir += polynomialParameter * utilpow;
        }

        ir = (ir * (1e9 / MAXFACTOR)) / SECONDS_PER_YEAR; // has 1e27 decimals
    }
}"
    },
    "lib/euler-vault-kit/src/InterestRateModels/IIRM.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

/// @title IIRM
/// @custom:security-contact security@euler.xyz
/// @author Euler Labs (https://www.eulerlabs.com/)
/// @notice Interface of the interest rate model contracts used by EVault
interface IIRM {
    error E_IRMUpdateUnauthorized();

    /// @notice Perform potentially state mutating computation of the new interest rate
    /// @param vault Address of the vault to compute the new interest rate for
    /// @param cash Amount of assets held directly by the vault
    /// @param borrows Amount of assets lent out to borrowers by the vault
    /// @return Then new interest rate in second percent yield (SPY), scaled by 1e27
    function computeInterestRate(address vault, uint256 cash, uint256 borrows) external returns (uint256);

    /// @notice Perform computation of the new interest rate without mutating state
    /// @param vault Address of the vault to compute the new interest rate for
    /// @param cash Amount of assets held directly by the vault
    /// @param borrows Amount of assets lent out to borrowers by the vault
    /// @return Then new interest rate in second percent yield (SPY), scaled by 1e27
    function computeInterestRateView(address vault, uint256 cash, uint256 borrows) external view returns (uint256);
}
"
    },
    "lib/euler-vault-kit/src/EVault/shared/Constants.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

// Implementation internals

// asset amounts are shifted left by this number of bits for increased precision of debt tracking.
uint256 constant INTERNAL_DEBT_PRECISION_SHIFT = 31;
// max amount for Assets and Shares custom types based on a uint112.
uint256 constant MAX_SANE_AMOUNT = type(uint112).max;
// max debt amount fits in uint144 (112 + 31 bits).
// Last 31 bits are zeros to ensure max debt rounded up equals max sane amount.
uint256 constant MAX_SANE_DEBT_AMOUNT = uint256(MAX_SANE_AMOUNT) << INTERNAL_DEBT_PRECISION_SHIFT;
// proxy trailing calldata length in bytes.
// Three addresses, 20 bytes each: vault underlying asset, oracle and unit of account + 4 empty bytes.
uint256 constant PROXY_METADATA_LENGTH = 64;
// gregorian calendar
uint256 constant SECONDS_PER_YEAR = 365.2425 * 86400;
// max interest rate accepted from IRM. 1,000,000% APY: floor(((1000000 / 100 + 1)**(1/(86400*365.2425)) - 1) * 1e27)
uint256 constant MAX_ALLOWED_INTEREST_RATE = 291867278914945094175;
// max valid value of the ConfigAmount custom type, signifying 100%
uint16 constant CONFIG_SCALE = 1e4;

// Account status checks special values

// no account status checks should be scheduled
address constant CHECKACCOUNT_NONE = address(0);
// account status check should be scheduled for the authenticated account
address constant CHECKACCOUNT_CALLER = address(1);

// Operations

uint32 constant OP_DEPOSIT = 1 << 0;
uint32 constant OP_MINT = 1 << 1;
uint32 constant OP_WITHDRAW = 1 << 2;
uint32 constant OP_REDEEM = 1 << 3;
uint32 constant OP_TRANSFER = 1 << 4;
uint32 constant OP_SKIM = 1 << 5;
uint32 constant OP_BORROW = 1 << 6;
uint32 constant OP_REPAY = 1 << 7;
uint32 constant OP_REPAY_WITH_SHARES = 1 << 8;
uint32 constant OP_PULL_DEBT = 1 << 9;
uint32 constant OP_CONVERT_FEES = 1 << 10;
uint32 constant OP_LIQUIDATE = 1 << 11;
uint32 constant OP_FLASHLOAN = 1 << 12;
uint32 constant OP_TOUCH = 1 << 13;
uint32 constant OP_VAULT_STATUS_CHECK = 1 << 14;
// Delimiter of possible operations
uint32 constant OP_MAX_VALUE = 1 << 15;

// Config Flags

// When flag is set, debt socialization during liquidation is disabled
uint32 constant CFG_DONT_SOCIALIZE_DEBT = 1 << 0;
// When flag is set, asset is considered to be compatible with EVC sub-accounts and protections
// against sending assets to sub-accounts are disabled
uint32 constant CFG_EVC_COMPATIBLE_ASSET = 1 << 1;
// Delimiter of possible config flags
uint32 constant CFG_MAX_VALUE = 1 << 2;

// EVC authentication

// in order to perform these operations, the account doesn't need to have the vault installed as a controller
uint32 constant CONTROLLER_NEUTRAL_OPS = OP_DEPOSIT | OP_MINT | OP_WITHDRAW | OP_REDEEM | OP_TRANSFER | OP_SKIM
    | OP_REPAY | OP_REPAY_WITH_SHARES | OP_CONVERT_FEES | OP_FLASHLOAN | OP_TOUCH | OP_VAULT_STATUS_CHECK;
"
    }
  },
  "settings": {
    "remappings": [
      "ethereum-vault-connector/=lib/euler-vault-kit/lib/ethereum-vault-connector/src/",
      "euler-vault-kit/=lib/euler-vault-kit/src/",
      "euler-price-oracle/=lib/euler-price-oracle/",
      "permit2/=lib/euler-vault-kit/lib/permit2/",
      "forge-std/=lib/forge-std/src/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
      "openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
      "@openzeppelin/contracts/=lib/euler-vault-kit/lib/openzeppelin-contracts/contracts/",
      "@pendle/core-v2/=lib/euler-price-oracle/lib/pendle-core-v2-public/contracts/",
      "@pyth/=lib/euler-price-oracle/lib/pyth-sdk-solidity/",
      "@redstone/evm-connector/=lib/euler-price-oracle/lib/redstone-oracles-monorepo/packages/evm-connector/contracts/",
      "@solady/=lib/euler-price-oracle/lib/solady/src/",
      "@uniswap/v3-core/=lib/euler-price-oracle/lib/v3-core/",
      "@uniswap/v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/",
      "ds-test/=lib/forge-safe/lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
      "forge-gas-snapshot/=lib/euler-vault-kit/lib/permit2/lib/forge-gas-snapshot/src/",
      "forge-safe/=lib/forge-safe/",
      "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
      "morpho-blue/=lib/morpho-blue/",
      "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
      "openzeppelin/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/",
      "pendle-core-v2-public/=lib/euler-price-oracle/lib/pendle-core-v2-public/contracts/",
      "pyth-sdk-solidity/=lib/euler-price-oracle/lib/pyth-sdk-solidity/",
      "redstone-oracles-monorepo/=lib/euler-price-oracle/lib/",
      "solady/=lib/euler-price-oracle/lib/solady/src/",
      "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/euler-price-oracle/lib/v3-core/contracts/",
      "v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/contracts/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 20000
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": false
  }
}}

Tags:
Proxy, Yield, Upgradeable, Factory, Oracle|addr:0x26a79e3bc30638f92212a3349c185fa67f37d01c|verified:true|block:23378671|tx:0x390e051a0536cf85407a321005ec0167abe78588502e952255f6cd9150bba476|first_check:1758105014

Submitted on: 2025-09-17 12:30:15

Comments

Log in to comment.

No comments yet.