Skip to main content

Withdrawable

MidnightWithdrawableCallback backs a standing par buy (lend) offer on Morpho Midnight that is funded from a market's withdrawable liquidity. Lenders can exit at par and borrowers can borrow at 0%, up to the market's withdrawable amount.

Description​

The contract is the offer's maker, ratifier and callback. The offer needs no signature, so anyone can build it and take it directly through MORPHO_MIDNIGHT.take(), on any market. Takers need no approval or authorization for this contract.

Because the offer is a buy (lend) offer at par (price 1), the taker is always the seller:

  • Lenders resell their credit at par, which exits the position before maturity.
  • Borrowers open debt at 0% interest, receiving one loan token per unit of debt.

In both cases the seller receives the filled units minus Midnight's settlement fee. Each fill credits the contract with the filled units, and the callback withdraws them from the market in the same take. The contract never holds a position or a token balance between takes.

Step-by-step callback execution​

The offer is never posted: the taker builds it with this contract as maker, ratifier and callback, and takes it on Morpho Midnight.

MidnightWithdrawableCallback flow: a taker calls take() on the par buy offer on Morpho Midnight; the callback ratifies the offer, validates the fill, withdraws the filled units from the market's withdrawable, and Morpho Midnight pulls the loan tokens through a standing approval and pays the seller the units minus the settlement fee.
  1. Take: A taker fills the par buy (lend) offer on Morpho Midnight by calling MORPHO_MIDNIGHT.take() for a number of units.
  2. Ratify: Morpho Midnight calls isRatified on the contract. The offer must name the contract as maker and callback and be a buy offer (NotSelf otherwise), with tick == MAX_TICK, i.e. price 1 (NotPar otherwise).
  3. Validate: Morpho Midnight invokes onBuy on the callback. The callback checks that the caller is Morpho Midnight, that buyerAssets and units are non-zero, and that the buyer is the contract itself.
  4. Withdraw: The fill credits the contract with units. The callback immediately withdraws those units of loan token from the market's withdrawable. The take reverts if withdrawable(id) < units.
  5. Settle: Morpho Midnight pulls buyerAssets (equal to units at par) from the callback through a standing max approval of the loan token, set on the first fill.
  6. Pay the seller: The seller receives units minus Midnight's settlement fee. The contract ends the take with no position and no balance.

Example​

A lender holds 10,000 USDC of credit on a Midnight USDC market that still has weeks to maturity. Borrowers have repaid early, so the market has 50,000 USDC of withdrawable liquidity.

  1. The lender builds the par buy (lend) offer with MidnightWithdrawableCallback as maker, ratifier and callback, and takes it for 10,000 units.
  2. Midnight ratifies the offer and invokes onBuy. The contract is credited 10,000 units, and the callback withdraws 10,000 USDC from the market's withdrawable.
  3. Midnight pulls the 10,000 USDC from the callback and pays the lender 10,000 USDC minus the settlement fee.

The lender ends the take with the credit sold and the market's withdrawable down to 40,000 USDC. A borrower taking the same offer would instead open 10,000 USDC of debt at 0% interest and receive 10,000 USDC minus the settlement fee.

Offer parameters​

Midnight and isRatified enforce these offer fields:

  • maker, ratifier and callback: the MidnightWithdrawableCallback address.
  • buy: true.
  • tick: MAX_TICK (price 1).
  • reduceOnly: false, since the contract's credit always increases on a fill.
  • receiverIfMakerIsSeller: address(0) (enforced by Midnight).

The taker picks the other fields: market, group, start, expiry, maxUnits or maxAssets, and continuousFeeCap. callbackData is ignored.

Prerequisites​

None for the taker. The contract authorizes itself on Morpho Midnight in its constructor, which lets it pass the maker-to-ratifier check on take. It is not authorized for any other address.

Settlement fee on lender exits

A lender fill pays Midnight's settlement fee, which a direct call to Midnight's withdraw avoids. When the market's settlement fee is non-zero, a lender who can call withdraw directly is better off doing so. Use this callback for flows that can only take offers, such as router batches.

Offer groups are shared

consumed[maker][group] on Midnight is shared by every taker of the same group, and it can never be reset. Use maxUnits = type(uint128).max or a group unique to the taker, so other fills in the group cannot exhaust the offer.

Gated markets

In a gated market, every fill (lender exits included) requires the market's enter gate to let this contract increase its credit, and borrowers must be allowed to increase their debt. Lender exits are never gated on debt.

Borrower fills and bad debt

A borrower fill replaces withdrawable liquidity with new debt. If that debt later becomes bad debt, the loss is socialized among the market's lenders, as for any other debt on the market.