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

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:
ObjectID
DarkPoolVault<DUSDC>0xb27ebe51c41c395790528c69ea57b807b40b2dd476ab8b579d74884925ba616a
BalanceManager0x49334be6766560d69b067a40c0d6589b945367d1d86dce45201fd57dd8d37429
TradeCap (held by keeper)0xdd7f313e90ec2c14225f68d66368a98384b11584e4ac7740860111ea1fe2e015
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.