This page covers how a redemption is gated after the bond has matured.
For the bond minting and 730-day maturity rules, see
Claim Bonds.
BondVault enforces a per-epoch redemption throttle so that no single
epoch can drain the vault’s LUMINA reserve in one block, even if every
bond holder in that epoch tries to redeem simultaneously. The mechanism is
a fixed weekly cap (MAX_REDEMPTION_PER_EPOCH_BPS = 108) plus a FIFO
queue for the overflow.
The number
| Constant | Value |
|---|---|
MAX_REDEMPTION_PER_EPOCH_BPS | 108 (1.08% per epoch / per week) |
| Epoch length | 7 days (one week per epoch) |
| Worst-case drain time for a full epoch | ≈ 12 weeks (12 × 1.08% ≈ 12.96%) |
What it actually limits
The throttle limits the LUMINA paid out per epoch per week, expressed as a fraction of that epoch’s total minted face. Concretely, for an epoch withS units of face value minted:
FIFO queue — burn-now, deliver-later
When a redemption request exceeds the weekly throttle:- The bond tokens are burned at queue time (they leave the user’s wallet immediately — there is no “pending bond” balance to double-spend).
- A queue entry is created recording the holder, the USD amount, and the position in the FIFO line.
- When
processQueue()is called in a later epoch with available weekly allowance, the queue is drained in first-in-first-out order and LUMINA is delivered to the recorded holder addresses.
processQueue() is public — anyone can call it (a keeper, the
holder themselves, or an unrelated wallet). The function pays out as
many queue entries as the current week’s remaining allowance permits,
then stops. In practice, a keeper bot calls it once per epoch.
Why it exists
Two failure modes that the throttle defends against:- Bond-run. Without the throttle, a single big epoch (e.g. a correlated mass-trigger across all 6 shields during a crash) could let bond holders redeem every token at once, dumping the entire LUMINA reserve onto the open market in one block. The throttle spreads that sell pressure over weeks.
- Death spiral. If a flash drop both triggers bonds (good) and simultaneously drops the LUMINA/USD reference (bad), unbounded redemption would mint disproportionately more LUMINA per bond, which in turn pushes LUMINA down further, which mints even more on the next redemption, and so on. The throttle slows this feedback loop enough for the buyback / TWAP burn machinery to catch up.
Practical effects for agents
- Small redemptions are immediate. If you’re redeeming 1% of an epoch’s face or less and you’re early in the week, you get paid in the same transaction.
- Big redemptions are queued. You’ll receive a
RedemptionQueuedevent instead ofBondRedeemed. Your bonds are gone; LUMINA arrives later viaRedemptionProcessedevents. - Listen, don’t poll. Use webhooks (
bond_redeemed,redemption_processed) instead of pollingBondVaultstate — the exact epoch when your queue entry pays is data-dependent. - Anyone can
processQueue(). If you’re tired of waiting and the current epoch has unused allowance, you can callprocessQueue()yourself.
Reading throttle state
Worked example
An epoch mints1,000,000 units of face ($1M notional).
| Week | Max redeemable | Cumulative |
|---|---|---|
| 1 | 10,800 units | 1.08% |
| 2 | 10,800 | 2.16% |
| 3 | 10,800 | 3.24% |
| … | … | … |
| 12 | 10,800 | 12.96% |
Check Redemption Status Programmatically
The on-chain getters above (epochSupply, remainingAllowance,
queueLength) tell you the aggregate state of an epoch. To answer
the holder-centric question — “will my specific redemption clear now or
get queued, and if queued, when?” — use the SDK, which composes those
calls with the new throttle-dynamic views (availableCapacityRatioBps,
policiesPaused) into a single response.
Requires @lumina-org/sdk v0.7.0+.
status field is one of:
immediate— the entireusdAmountfits inside the current week’s remaining allowance; callingredeem()now pays out LUMINA in the same transaction.queued— the request exceeds remaining allowance; bonds will be burned and a FIFO entry created. UseestimatedReleaseDateandqueuePositionto set holder expectations.partial— part ofusdAmountfits in the current week, the rest queues. The SDK splits the response so you can show both halves in the UI.
(holder, epochIdBond) pair — not per
individual bondId — because Claim Bonds are
ERC-1155 fungible within an epoch. Two holders holding the same
epoch share one supply curve but are queued independently.
Auto-injection Mechanism
When BondVault’s available-capacity ratio drops to 50% or below, BondVault automatically pulls 10% of the CEX Reserve’s LUMINA balance into the vault on the nextredeem, issue, or
processQueue call. This widens the safety margin without requiring a
manual rebalance.
The injection is bounded by the actual CEX Reserve balance, so once
that’s depleted the mechanism becomes a no-op until ops refills the
reserve.
This is on-chain only — no off-chain keeper required. Every
state-changing call that touches BondVault evaluates the trigger,
emitting AutoInjectionTriggered(uint256 amountInjected, uint256 newCapacityBps) when it fires.
Floor-Price Soft Pause
If the LUMINA spot price drops at-or-below $0.005, BondVault flipspoliciesPaused = true. This is a signal that the SDK and frontend
should consume to refuse new policy purchases — the contract does
not enforce it inside CoverRouterV2, so any
integration that bypasses the SDK can still submit purchases on-chain.
Recovery requires the price to rise back to 120% of floor ($0.006) —
the hysteresis band avoids flapping at the boundary.
The SDK exposes this via status.policiesPaused (see the snippet
above), and any front-end calling getRedemptionStatus for UX checks
gets the flag for free.
Related audit fixes
- FIX C-3 + H-6 — solvency floor and LUMINA/USD snapshot alignment; the throttle does not override these — it sits on top of them.
- FIX M-11 —
BondVault.burnFromReserves5%-per-tx burn cap (the reserve guard the throttle complements). - Sprint Fix Audit Economic — the
availableCapacityRatioBps()/policiesPaused()views, the auto-injection trigger, and the SDKgetRedemptionStatus()composition described above.
See also
- Claim Bonds — the ERC-1155 token being redeemed.
- LUMINA token — what gets paid out.
- Lifecycle — where the throttle sits in the full flow.