Skip to main content

Delayed Liquidation Gate

The Delayed Liquidation Gate gives borrowers a grace period before a liquidation can be triggered onchain, reducing the risk of immediate liquidation the moment a position first becomes unhealthy. Lenders and borrowers can create a custom market that uses this gate by setting it as the liquidatorGate, after which Morpho Midnight only permits liquidations that originate from this contract.

The grace period gives borrowers a window to add collateral or repay debt before any liquidator can seize their position. For example, a borrower with a 30-day USDC position briefly crosses their liquidation LTV during a 5% ETH flash crash. Under a gate configured with a 1-hour grace period, the borrower has 60 minutes to restore position health before liquidation becomes possible.

Risk for lenders

Lenders filling offers configured with a delayed liquidation gate take on additional price risk. If collateral value falls sharply during the grace period, bad debt becomes more likely. The grace period must also be started onchain: until someone calls startGracePeriod, no liquidation can happen, even if the position is already unhealthy. Lenders can require lower LLTVs or higher rates to compensate for these risks.

Best suited to P2P matching

This gate is intended for peer-to-peer matched positions. It is less well suited to markets, and in particular does not work with markets that use Vault V2 shares as collateral: when those shares are routed through MidnightVaultExecutor, both liquidation call paths revert and the position can no longer be liquidated through this gate.

Delayed liquidation steps

Morpho Midnight calls canLiquidate(caller) before allowing any liquidation; the gate's implementation returns true only when caller == address(this), which forces liquidators to call liquidate() on the gate. The gate enforces the grace and liquidation windows, then forwards the call to Morpho Midnight and handles token flows in the resulting onLiquidate callback.

Delayed liquidation timeline: a keeper calls startGracePeriod when the borrower is unhealthy, opening a 6-hour grace period during which the borrower may heal. When the grace period ends, an equally long liquidation period opens, first as a 1-minute priority window reserved for the priorityLiquidator (if one was supplied), then as open liquidation where any caller may invoke liquidate().
  1. Start grace period: Once a borrower's debt exceeds the maximum allowed by collateral value and LLTV, any keeper can call startGracePeriod(id, borrower, priorityLiquidator) to begin the countdown. The call reverts if a grace or liquidation period is already active for this borrower-market pair; a new one can only start once the previous GRACE_PERIOD + LIQUIDATION_PERIOD window has fully elapsed. The call also reverts if liquidation is currently locked for the borrower on that market: startGracePeriod checks MORPHO_MIDNIGHT.liquidationLocked(marketId, borrower) and reverts if it returns true. The priorityLiquidator argument is optional and is explained in the priority window step below.
  2. Grace period: For GRACE_PERIOD seconds after the start, no caller, including the priorityLiquidator, can liquidate the position; any liquidate() call reverts with LiquidationNotAllowed. This window gives the borrower time to add collateral, repay debt, or otherwise restore position health before liquidation becomes possible.
  3. Priority window: When the grace period ends, the liquidation period opens. The optional priorityLiquidator supplied to startGracePeriod exists to incentivize keepers to make that call: if one was set and the borrower is still unhealthy, the first PRIORITY_PERIOD seconds (capped at 60 seconds) are reserved for that address, and any other caller is rejected with LiquidationNotAllowed. Leaving the argument empty opens liquidation to any caller immediately when the grace period ends.
  4. Open liquidation: After PRIORITY_PERIOD elapses (or immediately, if no priority liquidator was set), the rest of the LIQUIDATION_PERIOD is open to any caller. Liquidators call liquidate(market, collateralIndex, seizedAssets, repaidUnits, borrower, data) on the gate.
  5. Window expires: The entire liquidation window expires once GRACE_PERIOD + LIQUIDATION_PERIOD has elapsed since the start. After that, no caller can liquidate through this gate until a new grace period is started with a fresh startGracePeriod call.

The full process flow is: startGracePeriod → grace window (GRACE_PERIOD, no liquidation allowed) → priority sub-window (first PRIORITY_PERIOD seconds of the liquidation window, reserved for the designated liquidator) → open liquidation (remainder of LIQUIDATION_PERIOD, anyone can liquidate) → window expires (no further liquidation until a new grace period starts).

When liquidate() is called, the gate validates timing, then calls MORPHO_MIDNIGHT.liquidate(). Inside the resulting onLiquidate callback, the gate transfers seized collateral to the liquidator, optionally invokes the liquidator's own onLiquidate callback (when data is non-empty) so they can sell collateral for loan tokens, then pulls loan tokens from the liquidator and approves Morpho Midnight to reclaim them.

Position health is re-checked at the moment liquidate() is called, not when the grace period was started. Two consequences follow:

  • If the borrower restores health during the grace period and the position remains healthy through the liquidation window, no liquidation can occur, even though the gate's timing window is open. The liquidation call reverts on Morpho Midnight's health check, and a fresh startGracePeriod is required if the position becomes unhealthy again later.
  • If the borrower adds collateral during the grace period but the position falls back below the LLTV during the liquidation window, the position can be liquidated immediately, with no additional grace period.

The grace timer is not cleared when a borrower recovers to a healthy state. If the position becomes unhealthy again after recovery, the old timer persists rather than resetting. A new startGracePeriod call only becomes possible after the original GRACE_PERIOD + LIQUIDATION_PERIOD window has fully elapsed.

Post-maturity liquidations

After the market's maturity timestamp, the grace period mechanism is bypassed entirely. Any address can call liquidate() through the gate without a prior startGracePeriod call, regardless of grace or liquidation period state. The gate becomes permissionless post-maturity, which ensures lenders can recover funds from matured, unpaid positions.

Key Parameters

Parameters are set at deployment from the factory and are immutable.

  • GRACE_PERIOD: Duration in seconds that borrowers have to restore position health before liquidation becomes possible.
  • LIQUIDATION_PERIOD: Duration in seconds during which liquidation is allowed after the grace period expires.
  • PRIORITY_PERIOD: Duration at the start of the liquidation window during which only the priorityLiquidator supplied to startGracePeriod may call liquidate().

Factory Deployment

Delayed Liquidation Gates are deployed via the DelayedLiquidationGateFactory using CREATE2 for deterministic addresses.

Factory constraints (MIN_PERIOD is 60 seconds, MAX_PERIOD is 72 hours):

  • GRACE_PERIOD must be within [60 seconds, 72 hours][MIN_PERIOD, MAX_PERIOD].
  • PRIORITY_PERIOD must be within [0, 60 seconds][0, MIN_PERIOD].
  • LIQUIDATION_PERIOD must be within [PRIORITY_PERIOD + 60 seconds, 72 hours][PRIORITY_PERIOD + MIN_PERIOD, MAX_PERIOD]. There is no flat 60-second floor; the lower bound tracks PRIORITY_PERIOD, guaranteeing at least one minute of open-liquidation time after any priority window.