Settlement & waterfall
When a sale ends with a winning bid, settlement runs the same waterfall regardless of whether the auction was Dutch or English. The waterfall determines how the proceeds are distributed among three parties: MLKY (a sale commission), lenders (via the pool), and the defaulted borrower (the remainder).
The waterfall
Let proceeds be the winning bid and debt = principal + fixed_interest_due
be the loan's full payoff.
- Commission —
proceeds × 500 / 10,000, a flat 5% of gross, charged on every settlement including a shortfall. 0% when MLKY or an affiliate is the buyer. - The pool —
min(proceeds − commission, debt), applied to principal first, then interest, then fees. - The borrower —
(proceeds − commission) − debt, or zero if that would be negative.
The three always sum to exactly proceeds. That is asserted as a unit-test
invariant on the settlement function for every input: no rounding may leak
value in any direction.
A worked example
Suppose:
- The defaulted loan has principal $1,000 and fixed interest $24.66, so
debt = $1,024.66. - The card was appraised at $2,000 when the loan was written.
- The sale clears at $1,500.
Then:
| Proceeds | $1,500.00 |
| Commission (5% of gross) | $75.00 |
| Pool — principal | $1,000.00 |
| Pool — interest | $24.66 |
| Borrower residual | $400.34 |
The pool's NAV moves up by $1,024.66 − $1,000 = $24.66 — it recovers its
principal and earns its full interest, and that is the ceiling. MLKY earns
$75. The borrower gets $400.34 and loses the card.
If the same sale had cleared at exactly the debt ($1,024.66):
- Commission $51.23, pool $973.43, borrower $0.
- The pool is short $51.23. Clearing at the debt no longer makes the
lender whole, because the commission comes off the top. A lender is made
whole at
debt / 0.95, which is $1,078.59 here.
If the sale had cleared below the debt — the ordinary outcome, not an exotic one, because a Dutch price keeps falling past the debt down to the principal:
- At $900: commission $45, pool $855, borrower $0. NAV drops by the $169.66 shortfall.
Why the borrower receives the residual
It is not a concession, and it is not a marketing decision. Under UCC Article 9 the surplus is not the secured party's to allocate: §9-615(d) requires it to be paid to the debtor, and §9-602 puts that among the rules a debtor cannot waive — alongside the notice requirements, the commercially-reasonable-disposition requirement, and the right of redemption.
It also happens to be commercially better for MLKY. 5% of gross beats 50% of
surplus everywhere below proceeds = 1.111 × debt, and defaults concentrate
below that line: a borrower whose card still holds most of its appraised
value repays rather than walking away. Those are the liquidations the
protocol previously collected nothing on.
The full reasoning, including the numbers, is in ADR-004.
Where the residual sits
The residual is written as a credit account, not pushed to the borrower's wallet.
Settlement is permissionless — anyone may finish an ended sale. A push
transfer to a borrower whose token account is missing or frozen would let
one bad destination brick every settlement in the protocol. So settlement
writes a BorrowerCredit PDA seeded on ["borrower_credit", auction, borrower] rather than moving the money.
The borrower then collects it in a transaction of their own, with
claim_borrower_credit, whenever they like.
This mirrors BidCredit, which already solves exactly this problem for
outbid bidders, down to re-deriving the auction signer after the auction
account has been closed.
- The USDC stays in the sale's bid escrow, which settlement does not close.
- The credit records
created_at, the moment settlement wrote it. claim_borrower_creditis permissionless to call but pays only the borrower, and emitsBorrowerCreditClaimed { auction, borrower, amount }.- No interest accrues on a residual while it sits unclaimed.
The twelve-month claim window
A residual left unclaimed for twelve months can be taken by the protocol. This is the only instruction in the protocol that moves money away from a borrower, so it is worth stating exactly rather than in summary.
The window is BORROWER_CREDIT_CLAIM_SECS, which is 365 days measured
from the credit's created_at. Once it has passed, the protocol admin may
call sweep_expired_borrower_credit, which pays the residual to
config.protocol_fee_recipient, closes the credit account, and emits
BorrowerCreditReverted { auction, borrower, amount, created_at, reverted_at }.
Two details decide what this actually means for a borrower.
Your claim does not expire — the protocol's right to sweep begins.
claim_borrower_credit never reads the clock. There is no expiry check on
the borrower's path at all, so a residual stays claimable by its owner
indefinitely, including long after the twelve months are up. What changes at
twelve months is that the admin gains a competing right. The two paths
overlap rather than hand over: past the window a residual is claimable by
the borrower and sweepable by the admin, and whichever transaction lands
first wins.
That shape is deliberate. Making the borrower's own claim conditional on the clock would mean a borrower arriving a slot late found their money had become somebody else's, with nothing on chain to explain why. Consumer pledge law — Wis. §138.10(12) and 10 Pa. Code §67.13 — gives that year to the borrower, so the borrower's path is the one kept unconditional.
The admin cannot route it to themselves. The destination is constrained
to config.protocol_fee_recipient, not chosen by the signer. The admin
receives only the account's rent, having paid for the transaction.
On-chain mechanics
The settlement instruction (auction_settle) does the following in one
atomic transaction:
Binds the winner
auction_settleis permissionless:settleris an unconstrained signer and any wallet may finish an ended sale. What stops a random third party claiming someone else's card is not a signature but a constraint — the suppliedwinneraccount must equalauction.highest_bid.bidder, and the card is routed to that account's ATA. Requiring the winner to sign was the previous design; it let an absent winner hold the NFT, the escrow and the loan accounts hostage indefinitely.Computes the waterfall
Calls the same shared function used for both auction formats.
calculate_settlement_amountsreturns(commission, pool_amount, borrower_residual).Moves the USDC
Transfers
pool_amount + commissionout of the bid escrow — not the entire winning bid, which is what it used to transfer. The residual is deliberately left behind in the escrow, under theBorrowerCreditthe same instruction writes.Moves the NFT
Transfers the card from the loan's collateral vault to the winner's associated token account, signed by the loan authority program-derived address (PDA). This step has separate code paths for standard NFTs (freeze + transfer) and programmable NFTs (delegate + revoke).
Updates pool aggregates
Decrements the pool's
outstanding_principalby the loan's principal and theaccrued_interestby the loan's fixed interest, since both are now resolved.Closes loan and auction accounts
Frees the rent on the loan account, the auction account, the collateral vault and the locks — but not the bid escrow, which has to survive to hold unclaimed credits. All of the rent goes to the settler; the accounts carry
close = settlerrather than a refund to whoever originally paid. This is the keeper incentive, and it is attached to finishing a sale rather than starting one.
Settlement closing the loan account is also what ends the borrower's right
to redeem. loan_redeem_from_auction takes a Defaulted loan and closes
its auction, so redemption is available for as long as both accounts exist,
and after settlement neither does. The two race for the same accounts and
the loser finds them gone: a redemption after a completed sale fails on the
missing loan, a settlement after a redemption on the missing auction.
Note that redemption is bounded by settlement rather than by the sale
ending. There is no check that the sale is still live, so a borrower who
finds the money on the last day may still redeem while a winning bid stands
unsettled — that bidder is refunded as a BidCredit rather than paid in
collateral.
The protocol fees that accumulate on the pool are swept by a separate admin
instruction (protocol_withdraw_fees), which routes them to the protocol
fee recipient and a configured insurance fund split.
Read next
- Dutch auctions — the format whose bids feed this waterfall in production.
- No-bid outcome — what happens when there's no bid to settle.
- Default & liquidation — the borrower-side perspective, including the right to redeem.