Skip to main content
DeepBook V3’s explorer surfaces every trade with the trader’s BalanceManager id. For institutional positions that’s a leak: anyone watching the book can reverse-engineer who’s accumulating which side. The dark-pool vault sits in front of a per-market binary Settlement<COLLATERAL> object and trades on behalf of all depositors via a pooled BalanceManager + TradeCap held by the vault.

Vault lifecycle

Dark-pool vault lifecycle across OPEN and SETTLED states. OPEN: Depositor calls deposit and gets a DepositReceipt. The vault then pull_for_mints from the Binary Settlement, which mint_pairs YES + NO into a vault-held V3 BalanceManager. The BalanceManager place_limit_orders on V3 pools via TradeCap. The vault tracks record_mint (aggregate_cost) as internal accounting. SETTLED: triggered by mark_resolved firing. The keeper sweeps with cancel + withdraw + redeem, then calls receive_settlement which flips the vault state to SETTLED. Depositors then redeem their receipts for pro-rata DUSDC.

State: OPEN. Depositor to vault (DepositReceipt), vault pull_for_mint into Binary Settlement, mint_pair lands YES + NO in the vault's V3 BalanceManager, TradeCap places limit orders on V3 pools, record_mint keeps aggregate_cost in sync. State: SETTLED. mark_resolved fires, keeper sweeps (cancel + withdraw + redeem), receive_settlement flips state, depositors redeem receipts for pro-rata DUSDC.

On-chain story

  1. Users deposit collateral into a shared DarkPoolVault<T>. Each gets a DepositReceipt for their pro-rata share. The deposit tx is visible (you to vault), but it doesn’t reveal which side or which strike they’d bet on.
  2. The vault stores the ids of a DeepBook V3 BalanceManager and a TradeCap (off-vault, held by the keeper as Sui objects). It also stores the binary-market Settlement id this vault trades against.
  3. The vault itself doesn’t dispatch the V3 calls. The keeper’s PTB does, threading both the vault and the BalanceManager into a single atomic block.
  4. The keeper batches deposits, mints YES+NO pairs from the Settlement, and (optionally) sells one leg on the V3 pool. Each mint is mirrored by record_mint so the vault’s aggregate_cost stays in sync.
  5. At resolution the keeper calls settlement::redeem_{yes,no} on the winning side, hands the resulting DUSDC to the vault via receive_settlement, and users call redeem to claim their pro-rata share.

Tradeoffs vs trading directly on V3

  • Privacy. V3 explorer sees <vault BalanceManager> placed YES limit @ 62¢, not the user. The user-to-vault link is visible at deposit time, but the deposit-to-specific-V3-order link is not.
  • Latency. Deposits sit idle until the keeper’s next mint batch (scripts/keeper-tick.ts, runnable as a cron).
  • Liquidity. Pre-resolution exit requires the vault to either burn_pair or sell the held legs on V3, bounded by V3 pool depth.

Invariants

From dark_pool_tests.move:
  1. sum(receipt.shares) over all live receipts equals vault.total_shares.
  2. coin::value(balance) at any point equals sum(deposits) - sum(payouts).
  3. Post-settlement, sum(redeem outputs) == final_payout_micro (modulo BigInt truncation, bounded by N wei or less).
  4. record_mint never emits when state is not STATE_OPEN.

Out of scope for v0.3

  • Mid-life NAV re-computation (multi-market vaults).
  • Pre-resolution withdrawal at NAV minus haircut.
  • Multi-Settlement vaults.

Live testnet vault

For the wc26_euro_champion World Cup market: Verified flow: 2 DUSDC deposit, keeper minted 2 YES + 2 NO pairs into the vault’s BalanceManager (tx 5y5xkwbu…eg5i), then the BalanceManager rested 1.9 YES + 1.9 NO asks at $0.55 on real V3 books via TradeCap (tx 4wFHsCEU…). See Vault Deposit Walkthrough for the full UX.