Inside Ladder

How sealed USDC orders on Solana become one fixed rate per maturity, and what the V0 program does with every micro-USDC from commit to redemption.

Ladder lends USDC at a fixed rate for a fixed term. Once a week, three auctions run side by side, one each for 1, 4 and 12 weeks. Each collects sealed lend and borrow orders and clears them at a single rate: every filled lender earns it, every filled borrower pays it, for the whole term. Together the three clearing rates form a yield curve, published on-chain every Friday.

This is how the V0 program does it. Every formula below is the program's own integer arithmetic, and the worked example was computed with it. V0 has real limits, listed at the end.

A week on the chain clock

Each maturity has its own sequence of auctions. An auction stores three timestamps, all compared against Solana's Clock sysvar:

  • Bids close: Wednesday 16:00 UTC (commit_end)
  • Reveals close: Friday 12:00 UTC (reveal_end); clearing is allowed from that moment
  • Maturity: reveal_end plus the term (7, 28 or 84 days), shared by every loan in the auction

Fixed parameters:

  • Rate grid: 64 ticks of 0.25%, from 0.00% to 15.75%
  • Bond: 0.5% of the order, at least 1 USDC, returned on reveal
  • Collateral: SOL, 250% of the principal at commit
  • Repayment window: until maturity plus a grace period set at market creation

Five kinds of account

All state lives in program-derived addresses:

  • Market ["market"]: the admin, the USDC mint, the SOL price used for collateral, the grace period and the next auction index for each maturity.
  • Auction ["auction", weeks, index]: the schedule, two 64-bucket histograms, the clearing result and the settlement counters. It owns a USDC vault ["vault", auction] and a note mint ["notes", auction].
  • Order ["order", auction, owner, nonce]: side, amount, commitment, bond, revealed tick. A borrower's SOL collateral is held by the Order account itself, never in a shared pool.
  • Loan ["loan", order]: principal, fixed debt and collateral of a filled borrower.

Lender USDC sits in the auction's vault. No instruction moves value between auctions.

Sealing an order

A commit locks real assets. A lender locks the full USDC amount plus the bond. A borrower locks the bond in USDC and 250% of the principal in SOL. Side and size are public. The rate is not:

commitment = sha256( rate_bps as u16 little-endian ‖ salt (32 bytes) )

The 32-byte salt makes the 64 possible ticks impossible to brute-force. The app generates it in the browser and keeps it there until the reveal window opens; it never leaves the device before the reveal.

Reveal: two arrays, no order list

Between commit_end and reveal_end, each owner publishes the rate and salt. The program recomputes the hash, checks it against the commitment, checks the rate is on the grid, adds the amount to one bucket, supply[tick] for a lender or demand[tick] for a borrower, and returns the bond.

The auction never stores a list of orders. After the last reveal it holds two arrays of 64 integers, which is all clearing needs. An order that is not revealed in time is refunded after clearing, minus its bond.

Clearing in one instruction

For each tick t, supply S(t) is the sum of lender orders at or below t, and demand D(t) is the sum of borrower orders at or above t. The clearing rate is the lowest tick where S(t) and D(t) are both positive and S(t) ≥ D(t). The matched amount is D(t).

Clearing is permissionless after reveal_end, reads two fixed arrays and loops over 64 ticks. Its cost is the same for ten orders or ten thousand.

Example book. Lenders: A 200k at 5.25%, B 400k at 6.00%, C 300k at 6.75%. Borrowers: X 150k at 7.50%, Y 350k at 6.25%, Z 250k at 5.50%.

TickSupply S(t)Demand D(t)
5.25%200k750k
5.50%200k750k
5.75%200k500k
6.00%600k500k
6.25%600k500k

At 6.00% supply first covers demand. The auction clears at 6.00% with 500k matched. Z bid 5.50%, below the clearing rate, and does not fill.

Who fills

With this rule the borrower side is always the short side: every borrower at or above the clearing rate fills in full. Here X and Y take 500k.

Lenders fill from the cheapest ask up. The marginal tick m is the lowest tick where cumulative supply reaches the matched amount; here 6.00%. Below it, orders fill completely: A lends 200k. At it, the remaining 300k is shared pro rata across the tick, rounded up per order:

fill = ceil(amount × marginal_fill / supply[m])

B fills 300k of its 400k and gets 100k back. C, above the marginal tick, fills nothing.

The marginal ticks only decide who fills. A asked 5.25% and earns 6.00%. X bid 7.50% and pays 6.00%.

Claims and the fixed debt

A lender's claim mints notes equal to its fill, one note per USDC of principal, and refunds the unfilled amount. A filled borrower's claim sends the principal, moves the collateral onto a Loan account and records the debt:

debt = principal + ceil(principal × rate_bps × days / (10,000 × 365))

Simple interest on a 365-day year, rounded up per loan to the next micro-USDC so rounding never leaves the pool short. 10,000 USDC for 28 days at 6.00% owes 10,046.027398 USDC. In the example X owes 150,690.410959 and Y owes 351,610.958905. The debt is fixed at the claim: repaying on the first day costs the same as on the last, because the lenders' notes were priced on it.

Claims are permissionless, so a keeper can settle every order after clearing without anyone signing.

Repayment, default and notes

A borrower repays the full debt until maturity plus grace and gets the collateral back. After that, anyone can seize an unpaid loan's collateral into the auction's SOL pool.

Once maturity has passed and every order and loan is settled, notes become redeemable. Burning x notes pays a share of what the auction actually received:

usdc_out = floor(repaid_pool × x / notes_outstanding)
sol_out  = floor(sol_pool    × x / notes_outstanding)

Both pools and the outstanding count shrink with each redemption. In the example both loans repay, so the pool holds 502,301.369864 USDC against 500,000 notes. A redeems 200,000 notes first and receives 200,920.547945 USDC; B then redeems 300,000 and receives 301,380.821919. The pool ends at zero.

Off-chain: keeper, RPC, tests

The keeper runs every hour. It opens each week's auctions, clears them after reveals and settles orders through the permissionless instructions. It never holds user funds and never signs for a user.

The app reaches Solana through a same-origin route that forwards an allow-list of RPC methods and keeps the provider key on the server.

The program ships with twelve tests on LiteSVM, including a check that its clearing rate equals the reference implementation on three books, and an end-to-end run of the JavaScript client against a local validator: init, faucet, commit, reveal, a forged reveal rejected, clear, claim, loan, repayment.

Decisions

  • One clearing rate instead of pay-as-bid. When each order pays its own price, everyone shades toward a guess of the result. With one rate set by the whole book, bidding your true limit is the best strategy.
  • Histograms instead of an on-chain order book. Sixty-four buckets per side make clearing a fixed-cost loop that fits in one instruction at any participation.
  • Commit-reveal instead of an open book. In an open book the last order sees the others. Sealing the rate until bids close removes that edge; the bond makes skipping a reveal cost in proportion to size.
  • Three maturities instead of one. Weekly 1, 4 and 12-week auctions publish a term structure for USDC on Solana, not a single point.
  • Full-term interest on early repayment. The debt fixed at the claim is what notes are priced on. Prorating would tie every lender's payout to each borrower's timing.

What V0 leaves out

V0 is a working core, not a finished protocol. Before it carries real money it needs:

  • An oracle. Collateral is valued at a SOL price set by the admin, not by Pyth.
  • Liquidation before maturity. A loan that becomes under-collateralised mid-term is not liquidated; only default after maturity is handled.
  • More collateral. SOL only. LSTs and cbBTC are planned.
  • A bound commitment. The hash does not include the owner or the auction, so an order committed before the deadline can copy a rate once its source reveals.
  • A secondary market. Notes are transferable SPL tokens; there is no built-in book for them yet.
  • Decentralised admin and an audit. The admin opens auctions and sets the price. The code is not audited.

The public beta uses a test USDC with no value and symbolic collateral, so nothing real is at risk while these land.

The docs and the app are at https://ladderdefi.xyz