Titandocs

Deposits & withdrawals

The collateral lifecycle — on-chain in, engine-approved out, with a nonce that makes replay impossible.

Collateral movement is the one flow that is fully on-chain on both ends. Trading is off-chain; custody never is.

Deposit

User exercises a deposit on the Vault contract, locking collateral and producing a DepositReceipt.

The indexer observes the ledger transaction and writes it into deposit_receipts.

The engine sees inbound.deposit on its own ledger feed and credits the trading balance.

Deposits need no engine approval — the collateral is already locked. The only latency is ledger finality plus indexer lag.

curl 'http://localhost:4000/v1/users/Alice::1220…/deposits?limit=20'

Withdrawal

Withdrawal is a request/approve flow, because the engine must confirm the collateral is not backing an open position.

Request (wallet-signed, on-chain)

The user submits a WithdrawalRequest carrying the current withdrawal counter as a nonce.

Engine evaluates

inbound.withdrawal_request reaches the engine. It checks free collateral against margin requirements at the last committed batch.

Approve or reject (on-chain)

engine.approve_submitted or engine.reject_submitted. On approval the vault releases and the counter increments.

The counter is the replay defence. GET /v1/users/:party/withdrawal-counter returns the current nonce. Signing a request with a stale counter produces a request the vault will reject — which is exactly what stops a captured request from being resubmitted later.

Why withdrawals wait for a commitment

The engine can only safely release collateral it can prove is unencumbered, and the proof is the committed batch. A withdrawal evaluated against uncommitted fills could release margin backing a position that the ledger does not yet know closed.

So user-visible withdrawal latency ≈ time to next batch commitment + ledger finality. Shrinking it means shortening the cadence — see Batches & commitments.

Reads

RouteReturns
GET /v1/deposits?user&before&limitGlobal deposit receipts, paginated
GET /v1/withdrawals?user&status&limitGlobal withdrawal requests
GET /v1/users/:party/depositsScoped, user-only
GET /v1/users/:party/withdrawals?statusScoped, filterable by status
GET /v1/users/:party/withdrawal-counterCurrent nonce

Testnet faucet

On devnet the BFF exposes a self-service top-up, gated by FAUCET_ENABLED, a per-party cooldown, and a balance ceiling:

curl -X POST 'http://localhost:4000/v1/users/Alice::1220…/faucet'

A party can only faucet itself — the route is guarded by requireOwnParty.

On this page