// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {Math} from "@openzeppelin-contracts-5.6.1/utils/math/Math.sol"; import {Constants} from "./Constants.sol"; /// @title InterestMath /// @notice Simple (non-compounding) interest accrual. Interest is computed on-the-fly from the loan's /// stored `outstandingPrincipal`, `rate`, and `lastAccrualTimestamp` — there is no stored /// `accruedInterest`. The debt at any second is `outstandingPrincipal + accrued(...)`. /// @dev Rounds up: a borderline position reads as marginally more indebted, which is conservative for /// the lender both when servicing (interest is paid before principal) and when the same figure /// feeds the health-factor check at margin-call / liquidation time. library InterestMath { /// @notice Interest accrued on `outstandingPrincipal` at `rate` (annual, bps) over `elapsed` /// seconds, in principal base units. /// `accrued = outstandingPrincipal * rate * elapsed / (BPS * SECONDS_PER_YEAR)`. /// @dev `rate` (uint16 bps) and `elapsed` are small enough that `rate * elapsed` cannot overflow; /// `Math.mulDiv` carries the `outstandingPrincipal * (rate * elapsed)` product at full width. function accrued(uint256 outstandingPrincipal, uint16 rate, uint256 elapsed) internal pure returns (uint256) { return Math.mulDiv( outstandingPrincipal, uint256(rate) * elapsed, Constants.BPS * Constants.SECONDS_PER_YEAR, Math.Rounding.Ceil ); } }