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.
- Take: A taker fills the par buy (lend) offer on Morpho Midnight by calling
MORPHO_MIDNIGHT.take()for a number ofunits. - Ratify: Morpho Midnight calls
isRatifiedon the contract. The offer must name the contract asmakerandcallbackand be a buy offer (NotSelfotherwise), withtick == MAX_TICK, i.e. price 1 (NotParotherwise). - Validate: Morpho Midnight invokes
onBuyon the callback. The callback checks that the caller is Morpho Midnight, thatbuyerAssetsandunitsare non-zero, and that the buyer is the contract itself. - Withdraw: The fill credits the contract with
units. The callback immediately withdraws thoseunitsof loan token from the market's withdrawable. The take reverts ifwithdrawable(id) < units. - Settle: Morpho Midnight pulls
buyerAssets(equal tounitsat par) from the callback through a standing max approval of the loan token, set on the first fill. - Pay the seller: The seller receives
unitsminus 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.
- The lender builds the par buy (lend) offer with
MidnightWithdrawableCallbackasmaker,ratifierandcallback, and takes it for 10,000 units. - 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. - 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,ratifierandcallback: theMidnightWithdrawableCallbackaddress.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.
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.
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.
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.
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.