Design principle
Write as little as possible. Morpho Blue, Midnight, Vault V2 and
Tenor routing / callbacks / ratifier / oracle / gates already exist and are audited.
Ladder = series state machine + per-entity pro-rata accounting on top.
End-to-end flow
Ladder (custom)
Morpho (existing)
Tenor (existing)
Group entity
Treasury deposits USDC
From that legal entity’s book, during the window
Ladder · core
SeriesVault
Mints entity shares · NAV-based pro-rata · enforces state machine
Morpho · existing
Vault V2 (ERC-4626)
Pooled USDC earns variable Blue yield while waiting for a match
Ladder · orchestrator
SeriesMatcher
Posts lend offer via Tenor · triggers Vault→Midnight callback on fill
Morpho · existing
Midnight position
Fixed-rate, fixed-term loan · SeriesVault is the sole lender of record
EarlyExitHandler
Per-entity early exit via unit sale
SeriesRatifierAdapter
Auto-renewal at maturity (thin Tenor wrapper)
SeriesVault redeem()
Pull-based pro-rata payout at maturity
Series state machine
SUBSCRIPTION_OPEN
→
POOLING_CLOSED
→
MATCHED_ACTIVE
→
MATURED_SETTLED
Branches: PARTIALLY_MATCHED
· UNMATCHED_TIMEOUT
— withdraw blocked during pooling/match; early exit is a separate path.
Two accounting layers (keep separate)
Layer 1 · What Midnight sees
One lender, one address, one position. Midnight has no visibility into which group entity owns which share.
SeriesVault = lender of record
Layer 2 · What SeriesVault tracks
Share-token accounting inside SeriesVault. Each legal entity holds series shares = its claim on the pooled FX float.
5 entities → 1 Midnight position
Contracts to write
1
SeriesVaultCore · most new work
- Subscription window · mint series shares (NAV-based)
- Deposit pooled USDC into Morpho Vault V2 on close
- State transitions · block normal withdraw when matched
- Pro-rata redemption at maturity · partial/timeout splits
2
SeriesMatcherMost complex policy logic
- Post lend offer at target rate/term when pooling closes
- Detect fills via TenorRouter · trigger Vault→Midnight migration
- Report match results back to SeriesVault
- Timeout · re-offer or cancel per product policy
3
SeriesRatifierAdapterThin wrapper
- Series-level auto-renewal rules → Tenor Ratifier / IntentSettler interface
4
EarlyExitHandler
- Pro-rata credit-unit sale before maturity
- Expose estimated exit proceeds vs fixed rate to maturity separately
5
SeriesRegistry / FactoryAdministrative
- Deploy series · track active/past for frontend discovery
Do not build (reuse existing)
Morpho Blue / Midnight / Vault V2 core
TenorRouter · TenorAdapter · migration callbacks
Base Ratifier / IntentSettler framework
Oracles (Tenor Oracle with Validation)
Liquidation logic (Morpho/Midnight)
Gate/allowlist (Tenor Vault V2 Allowlist Gate)
At maturity
1
Loan settles → lump sum to SeriesVault
Keeper flips state to MATURED_SETTLED (automatic detection).
2
Path A — auto-renewal
Route share into next fixed rung via ratifier — no redeem.
3
Path B — pull-based redeem()
Each entity claims USDC · shares burned · isolated txs.
4
Path C — reinvest to Vault V2
Opt-in · keep series shares · pool vault.deposit() → BUFFER_ACTIVE · variable APY.
5
Optional: sponsored keeper
Feels automatic to the desk; still one tx per entity (no push loop).
Never push-pay every entity in one loop — unbounded gas + one bad address blocks the whole group.
Security (non-negotiable)
- Independent audit before real capital
- Formal verification on pro-rata accounting invariant
- Prove: Σ entity claims = total pool value
- Bug bounty scaled to TVL
- Capped pilot series first (small size, short term)
Audit surface = orchestration + accounting — not lending/liquidation core.
Why Ladder exists
Morpho Blue rates are mechanically volatile (utilization-driven IRM curve).
An FX treasury cannot put a floating headline on cash that has to be there for a payout.
Ladder pools that pre-positioned float in Vault V2, locks a fixed rate on a
real Midnight match, and keeps each entity’s share so the group can still
report Singapore separately from the UK.