InterestRateModel

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/interest-rate-model/InterestRateModel.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import { AccessControlEnumerableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";

import { IIrm } from "moolah/interfaces/IIrm.sol";
import { IInterestRateModel } from "./interfaces/IInterestRateModel.sol";

import { UtilsLib } from "./libraries/UtilsLib.sol";
import { ErrorsLib } from "./libraries/ErrorsLib.sol";
import { ExpLib } from "./libraries/ExpLib.sol";
import { MathLib, WAD_INT as WAD } from "./libraries/MathLib.sol";
import { ConstantsLib } from "./libraries/ConstantsLib.sol";
import { MarketParamsLib } from "../moolah/libraries/MarketParamsLib.sol";
import { Id, MarketParams, Market } from "moolah/interfaces/IMoolah.sol";
import { MathLib as MoolahMathLib } from "moolah/libraries/MathLib.sol";

/// @title InterestRateModel
/// @author Lista DAO
contract InterestRateModel is UUPSUpgradeable, AccessControlEnumerableUpgradeable, IInterestRateModel {
  using MathLib for int256;
  using UtilsLib for int256;
  using MoolahMathLib for uint128;
  using MarketParamsLib for MarketParams;

  /* EVENTS */

  /// @notice Emitted when a borrow rate is updated.
  event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 rateAtTarget);

  /// @notice Emitted when the minimum cap is updated.
  event MinCapUpdate(uint256 oldMinCap, uint256 newMinCap);

  /// @notice Emitted when the borrow rate cap for a market is updated.
  event BorrowRateCapUpdate(Id indexed id, uint256 oldRateCap, uint256 newRateCap);

  /// @notice Emitted when the borrow rate floor for a market is updated.
  event BorrowRateFloorUpdate(Id indexed id, uint256 oldRateFloor, uint256 newRateFloor);

  /* IMMUTABLES */

  /// @inheritdoc IInterestRateModel
  address public immutable MOOLAH;

  /* STORAGE */

  /// @inheritdoc IInterestRateModel
  mapping(Id => int256) public rateAtTarget;

  /// @inheritdoc IInterestRateModel
  mapping(Id => uint256) public rateCap;

  /// @inheritdoc IInterestRateModel
  uint256 public minCap;

  /// @inheritdoc IInterestRateModel
  mapping(Id => uint256) public rateFloor;

  bytes32 public constant MANAGER = keccak256("MANAGER");
  bytes32 public constant BOT = keccak256("BOT");

  /* CONSTRUCTOR */

  /// @custom:oz-upgrades-unsafe-allow constructor
  /// @param moolah The address of the Moolah contract.
  constructor(address moolah) {
    require(moolah != address(0), ErrorsLib.ZERO_ADDRESS);
    _disableInitializers();
    MOOLAH = moolah;
  }

  /// @notice Constructor.
  /// @param admin The new admin of the contract.
  function initialize(address admin) public initializer {
    require(admin != address(0), ErrorsLib.ZERO_ADDRESS);

    __AccessControl_init();

    _grantRole(DEFAULT_ADMIN_ROLE, admin);
  }

  /* BORROW RATES */

  /// @inheritdoc IIrm
  function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256) {
    (uint256 avgRate, ) = _borrowRate(marketParams.id(), market);
    return avgRate;
  }

  /// @inheritdoc IIrm
  function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256) {
    require(msg.sender == MOOLAH, ErrorsLib.NOT_MOOLAH);

    Id id = marketParams.id();

    (uint256 avgRate, int256 endRateAtTarget) = _borrowRate(id, market);

    rateAtTarget[id] = endRateAtTarget;

    // Safe "unchecked" cast because endRateAtTarget >= 0.
    emit BorrowRateUpdate(id, avgRate, uint256(endRateAtTarget));

    return avgRate;
  }

  /// @dev Returns avgRate and endRateAtTarget.
  /// @dev Assumes that the inputs `marketParams` and `id` match.
  function _borrowRate(Id id, Market memory market) private view returns (uint256, int256) {
    // Safe "unchecked" cast because the utilization is smaller than 1 (scaled by WAD).
    int256 utilization = int256(
      market.totalSupplyAssets > 0 ? market.totalBorrowAssets.wDivDown(market.totalSupplyAssets) : 0
    );

    int256 errNormFactor = utilization > ConstantsLib.TARGET_UTILIZATION
      ? WAD - ConstantsLib.TARGET_UTILIZATION
      : ConstantsLib.TARGET_UTILIZATION;
    int256 err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivToZero(errNormFactor);

    int256 startRateAtTarget = rateAtTarget[id];

    int256 avgRateAtTarget;
    int256 endRateAtTarget;

    if (startRateAtTarget == 0) {
      // First interaction.
      avgRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET;
      endRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET;
    } else {
      // The speed is assumed constant between two updates, but it is in fact not constant because of interest.
      // So the rate is always underestimated.
      int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulToZero(err);
      // market.lastUpdate != 0 because it is not the first interaction with this market.
      // Safe "unchecked" cast because block.timestamp - market.lastUpdate <= block.timestamp <= type(int256).max.
      int256 elapsed = int256(block.timestamp - market.lastUpdate);
      int256 linearAdaptation = speed * elapsed;

      if (linearAdaptation == 0) {
        // If linearAdaptation == 0, avgRateAtTarget = endRateAtTarget = startRateAtTarget;
        avgRateAtTarget = startRateAtTarget;
        endRateAtTarget = startRateAtTarget;
      } else {
        // Formula of the average rate that should be returned to Moolah:
        // avg = 1/T * ∫_0^T curve(startRateAtTarget*exp(speed*x), err) dx
        // The integral is approximated with the trapezoidal rule:
        // avg ~= 1/T * Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / 2 * T/N
        // Where f(x) = startRateAtTarget*exp(speed*x)
        // avg ~= Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / (2 * N)
        // As curve is linear in its first argument:
        // avg ~= curve([Σ_i=1^N [f((i-1) * T/N) + f(i * T/N)] / (2 * N), err)
        // avg ~= curve([(f(0) + f(T))/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
        // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
        // With N = 2:
        // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + startRateAtTarget*exp(speed*T/2)] / 2, err)
        // avg ~= curve([startRateAtTarget + endRateAtTarget + 2*startRateAtTarget*exp(speed*T/2)] / 4, err)
        endRateAtTarget = _newRateAtTarget(startRateAtTarget, linearAdaptation);
        int256 midRateAtTarget = _newRateAtTarget(startRateAtTarget, linearAdaptation / 2);
        avgRateAtTarget = (startRateAtTarget + endRateAtTarget + 2 * midRateAtTarget) / 4;
      }
    }

    uint256 _cap = rateCap[id] != 0 ? rateCap[id] : ConstantsLib.DEFAULT_RATE_CAP;
    if (_cap < minCap) _cap = minCap;
    // Safe "unchecked" cast because avgRateAtTarget >= 0.
    uint256 avgRate = uint256(_curve(avgRateAtTarget, err));
    if (avgRate > _cap) avgRate = _cap;

    // Adjust rate to make sure the rate >= floor
    uint256 floor = rateFloor[id];
    if (avgRate < floor) avgRate = floor;

    return (avgRate, endRateAtTarget);
  }

  /// @dev Returns the rate for a given `_rateAtTarget` and an `err`.
  /// The formula of the curve is the following:
  /// r = ((1-1/C)*err + 1) * rateAtTarget if err < 0
  ///     ((C-1)*err + 1) * rateAtTarget else.
  function _curve(int256 _rateAtTarget, int256 err) private pure returns (int256) {
    // Non negative because 1 - 1/C >= 0, C - 1 >= 0.
    int256 coeff = err < 0 ? WAD - WAD.wDivToZero(ConstantsLib.CURVE_STEEPNESS) : ConstantsLib.CURVE_STEEPNESS - WAD;
    // Non negative if _rateAtTarget >= 0 because if err < 0, coeff <= 1.
    return (coeff.wMulToZero(err) + WAD).wMulToZero(int256(_rateAtTarget));
  }

  /// @dev Returns the new rate at target, for a given `startRateAtTarget` and a given `linearAdaptation`.
  /// The formula is: max(min(startRateAtTarget * exp(linearAdaptation), maxRateAtTarget), minRateAtTarget).
  function _newRateAtTarget(int256 startRateAtTarget, int256 linearAdaptation) private pure returns (int256) {
    // Non negative because MIN_RATE_AT_TARGET > 0.
    return
      startRateAtTarget.wMulToZero(ExpLib.wExp(linearAdaptation)).bound(
        ConstantsLib.MIN_RATE_AT_TARGET,
        ConstantsLib.MAX_RATE_AT_TARGET
      );
  }

  /// @dev Updates the borrow rate cap for a market. The new cap must be >= minCap.
  function updateRateCap(Id id, uint256 newRateCap) external onlyRole(BOT) {
    uint256 oldCap = rateCap[id];
    require(newRateCap >= minCap && newRateCap != oldCap, "invalid rate cap");
    require(rateFloor[id] <= newRateCap, "invalid new cap vs floor");

    rateCap[id] = newRateCap;

    emit BorrowRateCapUpdate(id, oldCap, newRateCap);
  }

  /// @dev Updates the minimum borrow rate for a market.
  function updateRateFloor(Id id, uint256 newRateFloor) external onlyRole(BOT) {
    uint256 oldFloor = rateFloor[id];
    require(newRateFloor != oldFloor, "invalid rate floor");

    // rate floor must be <= rate cap
    uint256 _cap = rateCap[id] != 0 ? rateCap[id] : ConstantsLib.DEFAULT_RATE_CAP;
    if (_cap < minCap) _cap = minCap;
    require(newRateFloor <= _cap, "invalid rate floor vs cap");

    rateFloor[id] = newRateFloor;

    emit BorrowRateFloorUpdate(id, oldFloor, newRateFloor);
  }

  /// @dev Updates the minimum borrow rate cap for all markets.
  function updateMinCap(uint256 newMinCap) external onlyRole(MANAGER) {
    uint256 oldMinCap = minCap;
    require(newMinCap > 0 && newMinCap != oldMinCap && newMinCap <= ConstantsLib.DEFAULT_RATE_CAP, "invalid min cap");
    minCap = newMinCap;

    emit MinCapUpdate(oldMinCap, newMinCap);
  }

  function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "../../interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/access/extensions/AccessControlEnumerableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/AccessControlEnumerable.sol)

pragma solidity ^0.8.20;

import {IAccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol";
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows enumerating the members of each role.
 */
abstract contract AccessControlEnumerableUpgradeable is Initializable, IAccessControlEnumerable, AccessControlUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControlEnumerable
    struct AccessControlEnumerableStorage {
        mapping(bytes32 role => EnumerableSet.AddressSet) _roleMembers;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlEnumerable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlEnumerableStorageLocation = 0xc1f6fe24621ce81ec5827caf0253cadb74709b061630e6b55e82371705932000;

    function _getAccessControlEnumerableStorage() private pure returns (AccessControlEnumerableStorage storage $) {
        assembly {
            $.slot := AccessControlEnumerableStorageLocation
        }
    }

    function __AccessControlEnumerable_init() internal onlyInitializing {
    }

    function __AccessControlEnumerable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].at(index);
    }

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) public view virtual returns (uint256) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].length();
    }

    /**
     * @dev Return all accounts that have `role`
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function getRoleMembers(bytes32 role) public view virtual returns (address[] memory) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        return $._roleMembers[role].values();
    }

    /**
     * @dev Overload {AccessControl-_grantRole} to track enumerable memberships
     */
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool granted = super._grantRole(role, account);
        if (granted) {
            $._roleMembers[role].add(account);
        }
        return granted;
    }

    /**
     * @dev Overload {AccessControl-_revokeRole} to track enumerable memberships
     */
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        AccessControlEnumerableStorage storage $ = _getAccessControlEnumerableStorage();
        bool revoked = super._revokeRole(role, account);
        if (revoked) {
            $._roleMembers[role].remove(account);
        }
        return revoked;
    }
}
"
    },
    "src/moolah/interfaces/IIrm.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { MarketParams, Market } from "./IMoolah.sol";

/// @title IIrm
/// @author Lista DAO
/// @notice Interface that Interest Rate Models (IRMs) used by Moolah must implement.
interface IIrm {
  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
  /// @dev Assumes that `market` corresponds to `marketParams`.
  function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
  /// storage.
  /// @dev Assumes that `market` corresponds to `marketParams`.
  function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}
"
    },
    "src/interest-rate-model/interfaces/IInterestRateModel.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { IIrm } from "moolah/interfaces/IIrm.sol";
import { Id } from "moolah/interfaces/IMoolah.sol";

/// @title IInterestRateModel
/// @author Lista DAO
/// @notice Interface exposed by the InterestRateModel.
interface IInterestRateModel is IIrm {
  /// @notice Address of Moolah.
  function MOOLAH() external view returns (address);

  /// @notice Rate at target utilization.
  /// @dev Tells the height of the curve.
  function rateAtTarget(Id id) external view returns (int256);

  /// @notice Rate cap for the given market.
  function rateCap(Id id) external view returns (uint256);

  /// @notice Minimum borrow rate for the given market.
  function rateFloor(Id id) external view returns (uint256);

  /// @notice Minimum borrow rate cap for all markets.
  function minCap() external view returns (uint256);

  /// @notice Updates the borrow rate cap for a market.
  function updateRateCap(Id id, uint256 newRateCap) external;

  /// @notice Updates the minimum borrow rate cap for all markets.
  function updateMinCap(uint256 newMinCap) external;
}
"
    },
    "src/interest-rate-model/libraries/UtilsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title UtilsLib
/// @author Lista DAO
/// @notice Library exposing helpers.
library UtilsLib {
  /// @dev Bounds `x` between `low` and `high`.
  /// @dev Assumes that `low` <= `high`. If it is not the case it returns `low`.
  function bound(int256 x, int256 low, int256 high) internal pure returns (int256 z) {
    assembly {
      // z = min(x, high).
      z := xor(x, mul(xor(x, high), slt(high, x)))
      // z = max(z, low).
      z := xor(z, mul(xor(z, low), sgt(low, z)))
    }
  }
}
"
    },
    "src/interest-rate-model/libraries/ErrorsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title ErrorsLib
/// @author Lista DAO
/// @notice Library exposing error messages.
library ErrorsLib {
  /// @dev Thrown when passing the zero address.
  string internal constant ZERO_ADDRESS = "zero address";

  /// @dev Thrown when the caller is not Moolah.
  string internal constant NOT_MOOLAH = "not Moolah";

  /// @notice Thrown when the caller is not the admin.
  string internal constant NOT_ADMIN = "not admin";
}
"
    },
    "src/interest-rate-model/libraries/ExpLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { WAD_INT } from "./MathLib.sol";

/// @title ExpLib
/// @author Lista DAO
/// @notice Library to approximate the exponential function.
library ExpLib {
  /// @dev ln(2).
  int256 internal constant LN_2_INT = 0.693147180559945309 ether;

  /// @dev ln(1e-18).
  int256 internal constant LN_WEI_INT = -41.446531673892822312 ether;

  /// @dev Above this bound, `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
  /// @dev This upper bound corresponds to: ln(type(int256).max / 1e36) (scaled by WAD, floored).
  int256 internal constant WEXP_UPPER_BOUND = 93.859467695000404319 ether;

  /// @dev The value of wExp(`WEXP_UPPER_BOUND`).
  int256 internal constant WEXP_UPPER_VALUE = 57716089161558943949701069502944508345128.422502756744429568 ether;

  /// @dev Returns an approximation of exp.
  function wExp(int256 x) internal pure returns (int256) {
    unchecked {
      // If x < ln(1e-18) then exp(x) < 1e-18 so it is rounded to zero.
      if (x < LN_WEI_INT) return 0;
      // `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
      if (x >= WEXP_UPPER_BOUND) return WEXP_UPPER_VALUE;

      // Decompose x as x = q * ln(2) + r with q an integer and -ln(2)/2 <= r <= ln(2)/2.
      // q = x / ln(2) rounded half toward zero.
      int256 roundingAdjustment = (x < 0) ? -(LN_2_INT / 2) : (LN_2_INT / 2);
      // Safe unchecked because x is bounded.
      int256 q = (x + roundingAdjustment) / LN_2_INT;
      // Safe unchecked because |q * ln(2) - x| <= ln(2)/2.
      int256 r = x - q * LN_2_INT;

      // Compute e^r with a 2nd-order Taylor polynomial.
      // Safe unchecked because |r| < 1e18.
      int256 expR = WAD_INT + r + (r * r) / WAD_INT / 2;

      // Return e^x = 2^q * e^r.
      if (q >= 0) return expR << uint256(q);
      else return expR >> uint256(-q);
    }
  }
}
"
    },
    "src/interest-rate-model/libraries/MathLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { WAD } from "moolah/libraries/MathLib.sol";

int256 constant WAD_INT = int256(WAD);

/// @title MathLib
/// @author Lista DAO
/// @notice Library to manage fixed-point arithmetic on signed integers.
library MathLib {
  /// @dev Returns the multiplication of `x` by `y` (in WAD) rounded towards 0.
  function wMulToZero(int256 x, int256 y) internal pure returns (int256) {
    return (x * y) / WAD_INT;
  }

  /// @dev Returns the division of `x` by `y` (in WAD) rounded towards 0.
  function wDivToZero(int256 x, int256 y) internal pure returns (int256) {
    return (x * WAD_INT) / y;
  }
}
"
    },
    "src/interest-rate-model/libraries/ConstantsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

/// @title ConstantsLib
/// @author Lista DAO
library ConstantsLib {
  /// @notice Curve steepness (scaled by WAD).
  /// @dev Curve steepness = 4.
  int256 public constant CURVE_STEEPNESS = 4 ether;

  /// @notice Adjustment speed per second (scaled by WAD).
  /// @dev The speed is per second, so the rate moves at a speed of ADJUSTMENT_SPEED * err each second (while being
  /// continuously compounded).
  /// @dev Adjustment speed = 50/year.
  int256 public constant ADJUSTMENT_SPEED = 50 ether / int256(365 days);

  /// @notice Target utilization (scaled by WAD).
  /// @dev Target utilization = 90%.
  int256 public constant TARGET_UTILIZATION = 0.9 ether;

  /// @notice Initial rate at target per second (scaled by WAD).
  /// @dev Initial rate at target = 4% (rate between 1% and 16%).
  int256 public constant INITIAL_RATE_AT_TARGET = 0.04 ether / int256(365 days);

  /// @notice Minimum rate at target per second (scaled by WAD).
  /// @dev Minimum rate at target = 0.1% (minimum rate = 0.025%).
  int256 public constant MIN_RATE_AT_TARGET = 0.001 ether / int256(365 days);

  /// @notice Maximum rate at target per second (scaled by WAD).
  /// @dev Maximum rate at target = 200% (maximum rate = 800%).
  int256 public constant MAX_RATE_AT_TARGET = 2.0 ether / int256(365 days);

  /// @dev Default borrow rate cap = 30% (scaled by WAD).
  uint256 public constant DEFAULT_RATE_CAP = uint256(0.3 ether) / 365 days;
}
"
    },
    "src/moolah/libraries/MarketParamsLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import { Id, MarketParams } from "../interfaces/IMoolah.sol";

/// @title MarketParamsLib
/// @author Lista DAO
/// @notice Library to convert a market to its id.
library MarketParamsLib {
  /// @notice The length of the data used to compute the id of a market.
  /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
  uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;

  /// @notice Returns the id of the market `marketParams`.
  function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
    assembly ("memory-safe") {
      marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
    }
  }
}
"
    },
    "src/moolah/interfaces/IMoolah.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

type Id is bytes32;

struct MarketParams {
  address loanToken;
  address collateralToken;
  address oracle;
  address irm;
  uint256 lltv;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
  uint256 supplyShares;
  uint128 borrowShares;
  uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
  uint128 totalSupplyAssets;
  uint128 totalSupplyShares;
  uint128 totalBorrowAssets;
  uint128 totalBorrowShares;
  uint128 lastUpdate;
  uint128 fee;
}

struct Authorization {
  address authorizer;
  address authorized;
  bool isAuthorized;
  uint256 nonce;
  uint256 deadline;
}

struct Signature {
  uint8 v;
  bytes32 r;
  bytes32 s;
}

/// @dev This interface is used for factorizing IMoolahStaticTyping and IMoolah.
/// @dev Consider using the IMoolah interface instead of this one.
interface IMoolahBase {
  /// @notice The EIP-712 domain separator.
  /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
  /// same chain id and on forks because the domain separator would be the same.
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  /// @notice The fee recipient of all markets.
  /// @dev The recipient receives the fees of a given market through a supply position on that market.
  function feeRecipient() external view returns (address);

  /// @notice Whether the `irm` is enabled.
  function isIrmEnabled(address irm) external view returns (bool);

  /// @notice Whether the `lltv` is enabled.
  function isLltvEnabled(uint256 lltv) external view returns (bool);

  /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
  /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
  function isAuthorized(address authorizer, address authorized) external view returns (bool);

  /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
  function nonce(address authorizer) external view returns (uint256);

  /// @notice Enables `irm` as a possible IRM for market creation.
  /// @dev Warning: It is not possible to disable an IRM.
  function enableIrm(address irm) external;

  /// @notice Enables `lltv` as a possible LLTV for market creation.
  /// @dev Warning: It is not possible to disable a LLTV.
  function enableLltv(uint256 lltv) external;

  /// @notice Sets the `newFee` for the given market `marketParams`.
  /// @param newFee The new fee, scaled by WAD.
  /// @dev Warning: The recipient can be the zero address.
  function setFee(MarketParams memory marketParams, uint256 newFee) external;

  /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
  /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
  /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
  /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
  function setFeeRecipient(address newFeeRecipient) external;

  /// @notice Creates the market `marketParams`.
  /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
  /// Moolah behaves as expected:
  /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
  /// - The token balance of Moolah should only decrease on `transfer` and `transferFrom`. In particular, tokens with
  /// burn functions are not supported.
  /// - The token should not re-enter Moolah on `transfer` nor `transferFrom`.
  /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
  /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
  /// - The IRM should not re-enter Moolah.
  /// - The oracle should return a price with the correct scaling.
  /// @dev Here is a list of properties on the market's dependencies that could break Moolah's liveness properties
  /// (funds could get stuck):
  /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
  /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
  /// `toSharesDown` overflow.
  /// - The IRM can revert on `borrowRate`.
  /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
  /// overflow.
  /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
  /// `liquidate` from being used under certain market conditions.
  /// - The price from the oracle must have 8 decimals.
  /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
  /// the computation of `assetsRepaid` in `liquidate` overflow.
  /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
  /// the point where `totalBorrowShares` is very large and borrowing overflows.
  function createMarket(MarketParams memory marketParams) external;

  /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
  /// `onMoolahSupply` function with the given `data`.
  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
  /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
  /// amount of shares is given for full compatibility and precision.
  /// @dev Supplying a large amount can revert for overflow.
  /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
  /// Consider using the `assets` parameter to avoid this.
  /// @param marketParams The market to supply assets to.
  /// @param assets The amount of assets to supply.
  /// @param shares The amount of shares to mint.
  /// @param onBehalf The address that will own the increased supply position.
  /// @param data Arbitrary data to pass to the `onMoolahSupply` callback. Pass empty data if not needed.
  /// @return assetsSupplied The amount of assets supplied.
  /// @return sharesSupplied The amount of shares minted.
  function supply(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    bytes memory data
  ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

  /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
  /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
  /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
  /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
  /// conversion roundings between shares and assets.
  /// @param marketParams The market to withdraw assets from.
  /// @param assets The amount of assets to withdraw.
  /// @param shares The amount of shares to burn.
  /// @param onBehalf The address of the owner of the supply position.
  /// @param receiver The address that will receive the withdrawn assets.
  /// @return assetsWithdrawn The amount of assets withdrawn.
  /// @return sharesWithdrawn The amount of shares burned.
  function withdraw(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    address receiver
  ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

  /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
  /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
  /// given for full compatibility and precision.
  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
  /// @dev Borrowing a large amount can revert for overflow.
  /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
  /// Consider using the `assets` parameter to avoid this.
  /// @param marketParams The market to borrow assets from.
  /// @param assets The amount of assets to borrow.
  /// @param shares The amount of shares to mint.
  /// @param onBehalf The address that will own the increased borrow position.
  /// @param receiver The address that will receive the borrowed assets.
  /// @return assetsBorrowed The amount of assets borrowed.
  /// @return sharesBorrowed The amount of shares minted.
  function borrow(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    address receiver
  ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

  /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
  /// `onMoolahRepay` function with the given `data`.
  /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
  /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
  /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
  /// roundings between shares and assets.
  /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
  /// @param marketParams The market to repay assets to.
  /// @param assets The amount of assets to repay.
  /// @param shares The amount of shares to burn.
  /// @param onBehalf The address of the owner of the debt position.
  /// @param data Arbitrary data to pass to the `onMoolahRepay` callback. Pass empty data if not needed.
  /// @return assetsRepaid The amount of assets repaid.
  /// @return sharesRepaid The amount of shares burned.
  function repay(
    MarketParams memory marketParams,
    uint256 assets,
    uint256 shares,
    address onBehalf,
    bytes memory data
  ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

  /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
  /// `onMoolahSupplyCollateral` function with the given `data`.
  /// @dev Interest are not accrued since it's not required and it saves gas.
  /// @dev Supplying a large amount can revert for overflow.
  /// @param marketParams The market to supply collateral to.
  /// @param assets The amount of collateral to supply.
  /// @param onBehalf The address that will own the increased collateral position.
  /// @param data Arbitrary data to pass to the `onMoolahSupplyCollateral` callback. Pass empty data if not needed.
  function supplyCollateral(
    MarketParams memory marketParams,
    uint256 assets,
    address onBehalf,
    bytes memory data
  ) external;

  /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
  /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
  /// @param marketParams The market to withdraw collateral from.
  /// @param assets The amount of collateral to withdraw.
  /// @param onBehalf The address of the owner of the collateral position.
  /// @param receiver The address that will receive the collateral assets.
  function withdrawCollateral(
    MarketParams memory marketParams,
    uint256 assets,
    address onBehalf,
    address receiver
  ) external;

  /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
  /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
  /// `onMoolahLiquidate` function with the given `data`.
  /// @dev Either `seizedAssets` or `repaidShares` should be zero.
  /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
  /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
  /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
  /// @param marketParams The market of the position.
  /// @param borrower The owner of the position.
  /// @param seizedAssets The amount of collateral to seize.
  /// @param repaidShares The amount of shares to repay.
  /// @param data Arbitrary data to pass to the `onMoolahLiquidate` callback. Pass empty data if not needed.
  /// @return The amount of assets seized.
  /// @return The amount of assets repaid.
  function liquidate(
    MarketParams memory marketParams,
    address borrower,
    uint256 seizedAssets,
    uint256 repaidShares,
    bytes memory data
  ) external returns (uint256, uint256);

  /// @notice Executes a flash loan.
  /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
  /// markets combined, plus donations).
  /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
  /// - `flashFee` is zero.
  /// - `maxFlashLoan` is the token's balance of this contract.
  /// - The receiver of `assets` is the caller.
  /// @param token The token to flash loan.
  /// @param assets The amount of assets to flash loan.
  /// @param data Arbitrary data to pass to the `onMoolahFlashLoan` callback.
  function flashLoan(address token, uint256 assets, bytes calldata data) external;

  /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
  /// @param authorized The authorized address.
  /// @param newIsAuthorized The new authorization status.
  function setAuthorization(address authorized, bool newIsAuthorized) external;

  /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
  /// @dev Warning: Reverts if the signature has already been submitted.
  /// @dev The signature is malleable, but it has no impact on the security here.
  /// @dev The nonce is passed as argument to be able to revert with a different error message.
  /// @param authorization The `Authorization` struct.
  /// @param signature The signature.
  function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

  /// @notice Accrues interest for the given market `marketParams`.
  function accrueInterest(MarketParams memory marketParams) external;

  /// @notice Adds `account` to the liquidation whitelist of the market `id`.
  function addLiquidationWhitelist(Id id, address account) external;

  /// @notice Removes `account` from the liquidation whitelist of the market `id`.
  function removeLiquidationWhitelist(Id id, address account) external;

  /// @notice Add/removes `accounts` from the liquidation whitelist of markets `ids`.
  function batchToggleLiquidationWhitelist(Id[] memory ids, address[][] memory accounts, bool isAddition) external;

  /// @notice Returns the liquidation whitelist of the market `id`.
  function getLiquidationWhitelist(Id id) external view returns (address[] memory);

  /// @notice Returns whether `account` is in the liquidation whitelist of the market `id`.
  function isLiquidationWhitelist(Id id, address account) external view returns (bool);
  /// @notice Set the minimum loan token assets(USD) (supply and borrow).
  function setMinLoanValue(uint256 minLoan) external;

  /// @notice get the minimum loan token assets (supply and borrow) for the market.
  function minLoan(MarketParams memory marketParams) external view returns (uint256);

  /// @notice add a new provider for the token.
  function addProvider(Id id, address provider) external;

  /// @notice remove the provider for the token.
  function removeProvider(Id id, address token) external;

  /// @notice get the provider for the market.
  function providers(Id id, address token) external view returns (address);

  /// @notice Return the whitelist of the market `id`.
  function getWhiteList(Id id) external view returns (address[] memory);

  /// @notice Returns `true` if `account` is whitelisted of market `id`.
  function isWhiteList(Id id, address account) external view returns (bool);

  /// @notice Add `account` to the whitelist of the market `id`.
  function addWhiteList(Id id, address account) external;

  /// @notice Remove `account` from the whitelist of the market `id`.
  function removeWhiteList(Id id, address account) external;

  /// @notice Returns the default market fee.
  function defaultMarketFee() external view returns (uint256);

  /// @notice Set the default market fee for new markets.
  function setDefaultMarketFee(uint256 newFee) external;
}

/// @dev This interface is inherited by Moolah so that function signatures are checked by the compiler.
/// @dev Consider using the IMoolah interface instead of this one.
interface IMoolahStaticTyping is IMoolahBase {
  /// @notice The state of the position of `user` on the market corresponding to `id`.
  /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
  /// accrual.
  function position(
    Id id,
    address user
  ) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

  /// @notice The state of the market corresponding to `id`.
  /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
  /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
  /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
  /// accrual.
  function market(
    Id id
  )
    external
    view
    returns (
      uint128 totalSupplyAssets,
      uint128 totalSupplyShares,
      uint128 totalBorrowAssets,
      uint128 totalBorrowShares,
      uint128 lastUpdate,
      uint128 fee
    );

  /// @notice The market params corresponding to `id`.
  /// @dev This mapping is not used in Moolah. It is there to enable reducing the cost associated to calldata on layer
  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
  function idToMarketParams(
    Id id
  ) external view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);

  /// @notice Returns whether the position of `borrower` in the given market `marketParams` is healthy.
  function isHealthy(MarketParams memory marketParams, Id id, address borrower) external view returns (bool);
}

/// @title IMoolah
/// @author Lista DAO
/// @dev Use this interface for Moolah to have access to all the functions with the appropriate function signatures.
interface IMoolah is IMoolahBase {
  /// @notice The state of the position of `user` on the market corresponding to `id`.
  /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
  /// accrual.
  function position(Id id, address user) external view returns (Position memory p);

  /// @notice The state of the market corresponding to `id`.
  /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
  /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
  /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
  /// interest accrual.
  function market(Id id) external view returns (Market memory m);

  /// @notice The market params corresponding to `id`.
  /// @dev This mapping is not used in Moolah. It is there to enable reducing the cost associated to calldata on layer
  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
  function idToMarketParams(Id id) external view returns (MarketParams memory);

  function getPrice(MarketParams calldata marketParams) external view returns (uint256);

  /// @notice grants `role` to `account`.
  function grantRole(bytes32 role, address account) external;
}
"
    },
    "src/moolah/libraries/MathLib.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Lista DAO
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
  /// @dev Returns (`x` * `y`) / `WAD` rounded down.
  function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
    return mulDivDown(x, y, WAD);
  }

  /// @dev Returns (`x` * `WAD`) / `y` rounded down.
  function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
    return mulDivDown(x, WAD, y);
  }

  /// @dev Returns (`x` * `WAD`) / `y` rounded up.
  function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
    return mulDivUp(x, WAD, y);
  }

  /// @dev Returns (`x` * `y`) / `d` rounded down.
  function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
    return (x * y) / d;
  }

  /// @dev Returns (`x` * `y`) / `d` rounded up.
  function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
    return (x * y + (d - 1)) / d;
  }

  /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
  /// continuous compound interest rate.
  function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
    uint256 firstTerm = x * n;
    uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
    uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);

    return firstTerm + secondTerm + thirdTerm;
  }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC-1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.22;

import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This library provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
 */
library ERC1967Utils {
    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit IERC1967.Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the ERC-1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit IERC1967.AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the ERC-1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit IERC1967.BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlEnumerable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/extensions/IAccessControlEnumerable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev External interface of AccessControlEnumerable declared to support ERC-165 detection.
 */
interface IAccessControlEnumerable is IAccessControl {
    /**
     * @dev Returns one of the accounts that have `role`. `index` must be a
     * value between 0 and {getRoleMemberCount}, non-inclusive.
     *
     * Role bearers are not sorted in any particular way, and their ordering may
     * change at any point.
     *
     * WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
     * you perform all queries on the same block. See the following
     * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
     * for more information.
     */
    function getRoleMember(bytes32 role, uint256 index) external view returns (address);

    /**
     * @dev Returns the number of accounts that have `role`. Can be used
     * together with {getRoleMember} to enumerate all bearers of a role.
     */
    function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
"
    },
    "lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) 

Tags:
ERC165, Proxy, Swap, Liquidity, Upgradeable, Factory, Oracle|addr:0xafdc06b831d117a773556656c1c1cd1a1fb96444|verified:true|block:23579741|tx:0x26b9df99ef0933c6268b2efee49e4b7f2e7b51d211e2c3982eac489512d5c91b|first_check:1760516013

Submitted on: 2025-10-15 10:13:34

Comments

Log in to comment.

No comments yet.