Skip to main content
Position-privacy vault for V3 binary markets. Sits in front of a per-market binary Settlement<COLLATERAL> object. Multiple depositors share an anonymity set; the vault (not the depositors) is the trader on DeepBook V3 pools.

Vault

public struct DarkPoolVault<phantom T> has key {
    id: UID,
    label: String,
    binary_market_id: ID,            // Settlement<T> this vault trades against
    expiry_ms: u64,                  // cached expiry mirror
    balance_manager_id: ID,          // DeepBook V3 BalanceManager (off-vault)
    trade_cap_id: ID,                // DeepBook V3 TradeCap (held by keeper)
    balance: Balance<T>,              // idle collateral
    total_shares: u64,
    aggregate_cost_micro: u64,
    state: u8,                       // STATE_OPEN | STATE_SETTLED
    final_payout_micro: u64,
    keeper: address,
    min_deposit_micro: u64,
}
States:
const STATE_OPEN: u8 = 0;
const STATE_SETTLED: u8 = 1;

Receipt

public struct DepositReceipt<phantom T> has key, store {
    id: UID,
    vault_id: ID,
    shares: u64,
    encrypted_attribution_blob_id: vector<u8>,    // Walrus blob (optional)
    attribution_hash: vector<u8>,                  // sha256 of plaintext
    seal_policy_id: ID,                            // gates decryption
}

Lifecycle functions

Create (admin)

public fun create_vault<T>(
    label: String,
    binary_market_id: ID,
    expiry_ms: u64,
    balance_manager_id: ID,
    trade_cap_id: ID,
    min_deposit_micro: u64,
    ctx: &mut TxContext,
): VaultAdminCap
Shares the vault; returns VaultAdminCap to the keeper. See scripts/create-darkpool-vault.ts.

Deposit

public fun deposit<T>(
    vault: &mut DarkPoolVault<T>,
    coin_in: Coin<T>,
    seal_policy: &AccessPolicy,
    encrypted_blob_id: vector<u8>,
    attribution_hash: vector<u8>,
    _clock: &Clock,
    ctx: &mut TxContext,
): DepositReceipt<T>
Gates: vault.state == OPEN, attribution_hash.length() == 32, coin_in.value >= min_deposit_micro.

Keeper: pull + mint accounting

public fun pull_for_mint<T>(
    vault: &mut DarkPoolVault<T>,
    cap: &VaultAdminCap,
    amount: u64,
    ctx: &mut TxContext,
): Coin<T>

public fun record_mint<T>(
    vault: &mut DarkPoolVault<T>,
    cap: &VaultAdminCap,
    market_key_id: ID,
    qty: u64,
    cost_micro: u64,
    _ctx: &TxContext,
)
Pair them in the same PTB: pull → settlement::mint_pair → BM deposit → record_mint.

Settlement

public fun receive_settlement<T>(
    vault: &mut DarkPoolVault<T>,
    cap: &VaultAdminCap,
    payout: Coin<T>,
    _ctx: &TxContext,
)

public fun redeem<T>(
    vault: &mut DarkPoolVault<T>,
    receipt: DepositReceipt<T>,
    ctx: &mut TxContext,
): Coin<T>
receive_settlement accepts the post-redeem DUSDC payout and flips state → SETTLED. Users then call redeem to claim their pro-rata share.

Pro-rata math

payout = (shares * vault.final_payout_micro) / vault.total_shares

Errors

ConstCodeMeaning
ENotAdmin1Cap doesn’t match vault
EVaultClosed2State != OPEN
EBelowMinDeposit3Coin value < min_deposit_micro
ENoShares4Receipt shares == 0
ESettlementNotReady5State != SETTLED on redeem
EAlreadySettled6receive_settlement after first call
EHashLenMismatch7Attribution hash not 32 bytes

Tests

tests/dark_pool_tests.move. 6 tests: full lifecycle with pro-rata payout, min-deposit abort, redeem-before-settlement abort, admin-gated record_mint, double-settle abort, receipt views.