A tokenized, auto-rolling range-ladder vault on DeepBook Predict: deposit dUSDC, the vault runs a strip of Predict positions around the at-the-money strike each expiry, auto-rolls on settlement, and issues a transferable share token (RVLT) so the LP position is portable collateral across Sui DeFi.
- Users deposit dUSDC and receive RVLT share tokens (a real Sui
Coin, transferable and composable). - Each expiry the vault derives its range strip from the LIVE on-chain SVI surface: it reads the OracleSVI params, computes the per-period implied stdev
σ_period = sqrt(w_ATM), and places the UP/DOWN wings atforward · exp(±k·σ). The band width auto-adapts to implied vol. - A PLP-yield + crash-hedge overlay supplies quote into
predict::supply(earning pool yield via a realCoin<PLP>) and buys a deep-OTM DOWN binary as crash insurance, exposing net APY = PLP yield minus hedge cost. - A withdrawal queue lets LPs request redemption mid-cycle (when capital is deployed and the balance is illiquid); requests are honored at the next roll boundary at the fair share price prevailing at fulfillment.
- On settlement a permissionless keeper redeems the settled legs and advances the vault back to accepting deposits for the next expiry.
- NAV accounting is ERC4626-style:
total_nav = liquid balance + capital deployed in Predict, so share price tracks strategy PnL.
New package 0xe68a24cf9c88075fc44e0b4e9d47f1e3b81216471c2a40cb952cee347330d616, VaultState 0x5549d1cc7990c3e6390bc3d86b7439787bcc499e4b6585c012731927c43c7851, KeeperCap 0x3949e2530739f50b252e23975040146ea319d06c597c94d0677fdd09f39b60dd. 9/9 Move tests pass.
| Feature | Proof (real testnet tx) | Detail |
|---|---|---|
| SVI-derived dynamic strikes | EypYbZRQ |
OPEN strip: forward $62,873.84, σ_period 0.1238%, k=1.0. Strikes $62,951 / $62,796 provably equal F·exp(±1σ). Band recorded on-chain in PositionsOpened. |
| Settle the SVI strip | Dvn7onop |
Settled $62,729: UP OTM, DOWN ITM. Real PnL +65,884 micros; Rolled new_nav=5,065,884. |
PLP supply (real Coin<PLP>) |
26EqTCGJ |
Supplied 0.2 dUSDC into predict::supply, minted 199,587 PLP (object 0xa884f043...). |
| Withdrawal request (mid-cycle) | 6yU6W6Un |
Queued 1.0 RVLT while POSITIONS_OPEN; WithdrawalQueued emitted. |
| Withdrawal fulfilled at roll | DPYKVpCN |
Paid 1,013,176 micros at the POST-PROFIT price 1.013176 dUSDC/RVLT; queued LP earned the cycle gains. |
The crash-insurance hedge leg (deep-OTM −3σ DOWN binary) is dry-run-proven on the live surface (mintable at ask 0.0106, premium 527 micros).
All transactions are live on Sui testnet against the real DeepBook Predict contract 0xf5ea2b3749c65d6e56507cc35388719aadb28f9cab873696a2f8687f5c785138.
BTC spot $63,971. NAV before: 1.0 dUSDC. NAV after: 1.0 dUSDC (preserved across roll).
Expiry A (oracle 07:00 UTC, 0x551f89...):
| Step | Digest | Detail |
|---|---|---|
mark_positions_opened |
E42ZtZCE |
VaultState: ACCEPTING → POSITIONS_OPEN, deployed=500k micros |
| UP leg (strike $64,271) | 4NpXvtbk |
PositionMinted is_up=true qty=250k ask=0.0306 |
| DOWN leg (strike $63,671) | EAg3wmR1 |
PositionMinted is_up=false qty=250k ask=0.0151 |
Roll (vault state transition):
| Step | Digest | Detail |
|---|---|---|
mark_rolled |
AJu9CpPC |
Rolled event: payout=500000 new_nav=1000000; vault → ACCEPTING_DEPOSITS |
Expiry B (oracle 07:15 UTC, 0xe9876a..., next expiry):
| Step | Digest | Detail |
|---|---|---|
mark_positions_opened |
DsZP1wKt |
VaultState: ACCEPTING → POSITIONS_OPEN on new oracle |
| UP leg (strike $64,271) | 3UeKdjjb |
PositionMinted is_up=true qty=250k ask=0.0713 |
| DOWN leg (strike $63,671) | CJf23Hbn |
PositionMinted is_up=false qty=250k ask=0.0420 |
All 6 Predict mints emitted PositionMinted events on two distinct oracle objects. The roll tx emitted Rolled. Reproduce with DRY_ONLY=0 node scripts/auto-roll.mjs.
Roll settlement note: Settlement is end-to-end real. settle-svi-roll.mjs calls predict_manager::withdraw<DUSDC>(Coin<PLP>) and pipes the payout into vault::mark_rolled in the same PTB, so NAV is funded by actual Predict settlement yield, not a keeper subsidy. Proof: ArqKbffrBP8VhafzyP3JE39dJKptAk3CCFErJ8vZ94yA (settled ITM, payout from Predict, Rolled new_nav=93553463).
Range-ladder strip against Predict:
| Leg | Digest | Strike | Cost |
|---|---|---|---|
| UP | CkM6x5Qt |
$65,334 | 0.0208 dUSDC |
| DOWN | 9wRXjKq3 |
$64,734 | 0.0185 dUSDC |
RollVault Move package + tokenized share:
| Item | ID / digest |
|---|---|
| Package | 0x57f5dff445bfc6b69789196a41922be8f1a9df2d938568ea3f090d38ec8a1822 |
| VaultState (shared) | 0x2991916f7baf734874c82d026bf0b72bdc25f5d76731b3928d9eb8b32287f54c |
create_vault<DUSDC> |
AnX23q9n |
deposit<DUSDC> 1 dUSDC |
AP1qKqzG |
| RVLT share coin minted | 0xaf1c1a497baf12921a54bf6ffbd13e949668433d01e81eb9e2ce54cfef3a80b2 (1,000,000 RVLT) |
The Move package owns the tokenized vault accounting and the RVLT share token (generic over the quote coin T, self-contained, sui move test green). The live Predict position-opening (mint of the UP/DOWN binaries) runs in the PTB layer against the real deployed Predict contract, with mark_positions_opened / mark_rolled recording vault accounting so NAV always reflects capital deployed off-vault. This split keeps the on-chain accounting trustless and unit-tested while reusing the production Predict contract directly.
move/rollvault/sources/vault.move, VaultState, deposit/withdraw, NAV math, position/roll bookkeeping hooks, events.move/rollvault/sources/vault_share.move, RVLTCoinviacreate_currencyOTW.move/rollvault/tests/vault_tests.move, 4 unit tests (share math, proportional second deposit, withdraw, open+roll cycle).scripts/open-vault-position.mjs, real Predict range-strip mint.scripts/create-and-deposit.mjs, create vault + deposit on the deployed package.scripts/simulate.mjs, PnL simulation.
The Predict vol surface only quotes strikes within roughly ATM ±400 ticks ($400, ≈ ±0.6% of a $65k spot); beyond that, predict::assert_mintable_ask aborts with code 7. RollVault places its wings at SPREAD_TICKS = 300 ($300, ≈ ±0.46% ≈ ~1σ for a sub-hour binary). This is the strike-width hook the brief asks range-ladder vaults to expose. See INTEGRATION-NOTES.md.
The vault is the liquidity provider / the-other-side: each expiry it sells the two OTM wings of the range ladder, keeps the premium when BTC stays inside the ATM±1σ band, and pays a capped 1.0 on a breached wing. Pricing uses the SVI binary N(d2) model, anchored to the live on-chain ask observed in the qualification tx. Deterministic seed; run node scripts/simulate.mjs.
Monte Carlo (10,000 independent 20-cycle runs):
| Metric | Value |
|---|---|
| Expected 20-cycle return | +5.81% |
| Std dev of return | 8.80% |
| Profitable runs | 72.2% |
| Best / worst run | +35.98% / -28.51% |
The printed 20-cycle trace (deterministic seed) lands at +6.84% with 14/20 cycles in-range, which is representative of the Monte Carlo mean rather than a cherry-picked best case. A single trace is dominated by breach variance and can land anywhere in the ±9% band; the honest headline is the expected LP carry across many paths. The vault earns the taker overpay on short-dated wings, sized at 4% of NAV per wing per cycle. The simulation reports both the sample path and the full Monte Carlo distribution.
Live testnet NAV: Real testnet cycles fire every 15 minutes, but the NAV curve appears near-flat because taker flow on the testnet Predict venue is thin. The simulation models the vault's behavior on a liquid exchange; the testnet flatness is an artifact of thin market conditions, not a strategy flaw.
Apex is a PLP + crash-insurance vault: it supplies into predict::supply and buys OTM binaries as hedges. RollVault is different on three axes:
- Range ladder vs PLP supply, RollVault runs a structured strip of UP/DOWN wings around ATM, not raw PLP supply.
- Portable tokenized share, RollVault issues RVLT, a real Sui
Coin(proven minted on-chain above), so the vault position is composable collateral elsewhere in Sui DeFi. Apex's LP position is not a portable token. - Auto-roll via
predict::redeem_permissionless, any keeper can roll the vault at settlement without privileged access, vs manual settlement.
All digests are live on Sui testnet and verifiable on suiscan.xyz/testnet.
The vault issues transferable RVLT share tokens (real Sui Coin via create_currency OTW, proven minted: object 0xaf1c1a49..., deposit tx AP1qKqzG). NAV accounting follows total_nav = liquid_balance + capital_deployed, so share price tracks real Predict settlement yield, not a keeper subsidy. The withdrawal queue is O(1) gas and honored at the post-profit share price at roll boundary (proven: queued-at-POSITIONS_OPEN tx 6yU6W6Un, fulfilled-at-roll tx DPYKVpCN, payout 1,013,176 micros at 1.013176 dUSDC/RVLT).
Package 6bcfdb7 (pkg 0xc1caad11) implements a tranche waterfall: senior depositors receive principal-protection with a return floor, junior depositors absorb first losses in exchange for levered upside. Both tranches are portable Coin shares. Proven on testnet: TrancheRolled event with senior-capped=21, junior=29979.
scripts/anchor-nav.mjs (package 9dca779, pkg 0xea2dfa2e) builds a per-roll NAV report JSON, SHA256s it, uploads to Walrus testnet, and records the (blobId, sha256) on-chain. Anyone can re-fetch blob vstrWuKb and confirm the hash matches on-chain value 9fda61.... Anchor tx C5qhVFLA (success, NavReportAnchored emitted).
Package afcc5ad exposes a sigma_move_stress(moves) on-chain function that computes a per-tranche health score under a range of BTC sigma moves. The frontend reads these scores live from the vault's svi_band() output and renders a health gauge that updates with real on-chain state (reads live vault 0x6bc5533d).
The vault's mark_rolled entry is gated by a KeeperCap so any permissionless keeper can advance the vault at settlement without owner privilege. The cap also enforces a min_payout floor: an undersized settlement coin aborts EPayoutBelowMin rather than silently shrinking NAV. 56 autonomous rolls recorded on-chain from real Rolled events (keeper roll proof DAniiF7i).
npm install --ignore-scripts
# Simulation (no blockchain needed)
node scripts/simulate.mjs
# Move package: build + test
cd move/rollvault && sui move build && sui move test
# Auto-roll keeper: open strip on Expiry A, roll, open strip on Expiry B (dry-run by default)
node scripts/auto-roll.mjs
DRY_ONLY=0 node scripts/auto-roll.mjs
# Single range-strip (dry-run by default)
node scripts/open-vault-position.mjs
REUSE_MANAGER=0x26bcbea5ff7662f30a548b32c6af2340c00401de054c5c2cd5f5d5b1d01b083f DRY_ONLY=0 node scripts/open-vault-position.mjs
# Create vault + deposit on the deployed package
DRY_ONLY=0 node scripts/create-and-deposit.mjs