// SPDX-License-Identifier: MIT pragma solidity ^0.8.30; import {ExecutedTerms} from "../types/DataTypes.sol"; /// @title ILoanManager /// @notice Interface for the singleton that manages loans on the principal chain. interface ILoanManager { /// @notice A loan's full on-chain state. Collateral parameters are denormalized so the loan can /// be valued and liquidated without reading the CollateralManager. struct Loan { address borrower; address apn; // Collateral (denormalized) address collateralToken; uint256 collateralAmount; uint256 collateralChainId; uint8 collateralDecimals; // collateral token decimals, denormalized (may be a remote token) address oracle; bytes32 oracleType; uint32 maxStaleness; uint16 ltv; uint16 marginCallThreshold; uint16 liquidationThreshold; uint16 maxLiquidationBonus; // Principal & interest address principalToken; uint8 principalDecimals; // principal token decimals, denormalized at execution (packs with principalToken) uint256 principalChainId; uint256 outstandingPrincipal; uint16 rate; bytes32 rateType; uint16 interestTakeRateBps; // snapshotted from FeeRegistry at creation uint256 startTimestamp; uint256 duration; uint256 lastAccrualTimestamp; } /// @notice Per-loan liquidation-auction state. All-zero when the loan is healthy and untriggered. struct Auction { uint64 marginCallAt; // when the margin call was triggered (Zone 2 grace start); 0 = none uint64 liquidationAt; // when full liquidation was triggered (auction start); 0 = none } /// @notice Global, admin-tunable liquidation parameters. Every field is range-bounded in code so /// tuning changes only the auction's shape, never whether a healthy loan can be seized /// (that is fixed per-loan by the signed intent's MCT/LT/maxLiquidationBonus). struct RiskParams { uint32 auctionDuration; // seconds for the auction's time factor to reach 100% uint32 marginCallDeadline; // Zone 2 grace before partial liquidation opens, seconds uint32 maturityGracePeriod; // grace after maturity before default liquidation opens, seconds uint16 bMin; // starting bonus, bps uint16 hfFloor; // HF at which Zone 3 distress reaches 1.0, bps ratio uint16 restoreBuffer; // partial liquidation restores to MCT - restoreBuffer, bps } /// @notice Create a loan from derived terms: move principal (minus the origination fee), record /// the loan, and mint the lender an APN. Restricted to the orchestrator. /// @param apnName Display name for the loan's APN token (executor-supplied, cosmetic). /// @param apnSymbol Display symbol for the loan's APN token (executor-supplied, cosmetic). /// @return apn The APN token minted for this loan. function execute(bytes32 matchId, ExecutedTerms calldata terms, string calldata apnName, string calldata apnSymbol) external returns (address apn); /// @notice Repay up to the loan's current debt, applying the interest-first waterfall: interest is /// paid first and split between the treasury and the lender (recorded to the APN), the /// remainder reduces principal. Permissionless (funds pulled from the caller); an amount /// above the total owed is capped. Releases the collateral once principal reaches zero. /// Works during a live full-liquidation auction too, letting the borrower de-lever or fully /// close mid-auction. Interest is charged live for the whole elapsed window in every case, so /// repaying confers no interest advantage over a healthy loan and a seizure confers none over /// repaying. A partial payment that restores health clears whatever it cured — a margin call /// once back below the margin-call threshold, a pre-maturity liquidation trigger once back /// below the liquidation threshold (best-effort: skipped if the oracle is stale, never /// blocking the repayment); otherwise the loan stays liquidatable with the auction's bonus /// clock intact (a maturity default always stays liquidatable). /// @return interestPortion Interest covered by this payment (protocol take + lender interest). /// @return principalPortion Principal repaid by this payment. function repay(bytes32 matchId, uint256 amount) external returns (uint256 interestPortion, uint256 principalPortion); /// @notice Increase the loan's denormalized collateral amount by `amount`. Restricted to the /// CollateralManager, which calls it from {ICollateralManager-addCollateral} after it has /// custodied the additional collateral, keeping the two records in sync. Like a repayment, /// a top-up that restores health clears any margin call or pre-maturity liquidation trigger /// it cured (best-effort: skipped if the oracle is stale, never blocking the top-up). function increaseCollateral(bytes32 matchId, uint256 amount) external; /// @notice The not-yet-cash part of a loan's APN net asset value: value the note holders are owed /// but that has not been repaid into the note yet — outstanding principal plus the lender's /// mark-to-market accrued-but-unpaid interest (net of the protocol take), in principal base /// units. The APN prices redemptions as `unrealizedValue + cash on hand`. Goes to zero once /// principal is fully repaid (all value is then cash) or for an unknown loan. function unrealizedValue(bytes32 matchId) external view returns (uint256); /// @notice Open a Zone-2 margin call on a loan whose current LTV has crossed into /// `[marginCallThreshold, liquidationThreshold)`, starting the grace period. Permissionless. /// Reverts if the loan is outside that band or already margin-called or in a liquidation auction. function triggerMarginCall(bytes32 matchId) external; /// @notice Clear a margin call whose loan has recovered below `marginCallThreshold`, so a later /// re-crossing starts a fresh grace period. Permissionless. function clearMarginCall(bytes32 matchId) external; /// @notice Open a full-liquidation auction, starting the reverse-Dutch bonus clock. Interest keeps /// accruing live (the auction never pauses it). Valid when current LTV has crossed the /// liquidation threshold (Zone 3) or the loan is past maturity plus the grace period /// (maturity default). Permissionless. function triggerLiquidation(bytes32 matchId) external; /// @notice Clear a full-liquidation trigger whose loan has recovered below its liquidation threshold /// and is not past maturity, closing the auction. Interest was never paused, so the window it /// was open stays billed. Permissionless. function clearLiquidation(bytes32 matchId) external; /// @notice Zone-2 partial liquidation after the margin-call grace period: repay up to the amount /// that restores the position to `MCT - restoreBuffer`, seizing collateral worth the /// repayment plus the time/distress-scaled bonus. The loan persists — except on the knife /// edge where the restoring repayment settles the entire debt, which closes the loan and /// releases any remaining collateral. Permissionless; funds pulled from the caller. /// `repayAmount` is capped to the restoring maximum and the debt. /// @param minSeized Slippage guard on the receive side, expressed as a RATE quote: the collateral /// the caller expects for a full `repayAmount` fill. When the engine caps the fill (a /// smaller live restoring maximum after a competing partial, or the debt), the floor /// scales pro-rata with the actual `repaid`, so a fairly-priced smaller fill executes and /// only a worsened exchange rate (price/bonus moved against the caller) reverts. Pass 0 /// to accept any rate. /// @return repaid Principal-token amount actually repaid. /// @return collateralSeized Collateral base units transferred to the caller. function liquidatePartial(bytes32 matchId, uint256 repayAmount, uint256 minSeized) external returns (uint256 repaid, uint256 collateralSeized); /// @notice Full liquidation of a triggered loan: repay the live debt — or, if the collateral cannot /// cover debt plus bonus, as much as the collateral backs, writing down the shortfall — seize /// the corresponding collateral (bonus included), return any excess to the borrower, and close /// the loan. Permissionless; funds pulled from the caller. A trigger that predates the /// maturity default and whose distress has since recovered prices its bonus from the default /// deadline rather than its stale clock. /// @param maxRepay Slippage guard on the pay side: the largest repayment the caller will make; /// reverts if the required repayment exceeds it. /// @param minSeized Slippage guard on the receive side: the smallest collateral amount the caller /// will accept; reverts if the seize comes in below it. Together the two bound the /// caller's worst-case exchange rate against oracle/bonus movement between submission and /// inclusion. Pass 0 to accept any seize (required to close a dust debt whose seize /// rounds to zero). /// @return repaid Principal-token amount actually repaid. /// @return collateralSeized Collateral base units transferred to the caller. function liquidate(bytes32 matchId, uint256 maxRepay, uint256 minSeized) external returns (uint256 repaid, uint256 collateralSeized); /// @notice The loan's live gross debt — outstanding principal plus interest accrued to this /// second (the exact amount a full repayment would settle right now). Zero for an /// unknown or fully-repaid loan. Pure state read: no oracle involved. function debtOf(bytes32 matchId) external view returns (uint256); /// @notice One-read health snapshot for off-chain monitors: live debt, the collateral's value /// in principal base units at the live oracle price, the current LTV in bps, and the /// zone. `zone` is trigger eligibility, matching {triggerLiquidation} / /// {triggerMarginCall} exactly: 2 = liquidatable (LTV ≥ liquidation threshold or past /// the maturity-default deadline), 1 = margin-call band [MCT, LT), 0 = healthy. /// All-zero for an unknown or fully-repaid loan. Reverts on a stale or invalid oracle /// price, like every other health-dependent path — treat a revert as "no fresh /// classification available", never as healthy. function healthOf(bytes32 matchId) external view returns (uint256 debt, uint256 collateralValue, uint256 ltvBps, uint8 zone); /// @notice The liquidation-auction state for `matchId` (all-zero if healthy/untriggered). function auctionOf(bytes32 matchId) external view returns (Auction memory); /// @notice The global liquidation risk parameters. function riskParams() external view returns (RiskParams memory); /// @notice The loan recorded for `matchId` (zeroed if none). function loanOf(bytes32 matchId) external view returns (Loan memory); }