Balances & VTXOs

VTXOs

Wavelength tracks spendable funds the way Ark does: as off-chain outputs bundled into the operator’s periodic batch transactions, not as a single on-chain UTXO in your app. The sections below walk through that model, how boarding turns into live balance, and why confirmed and pending numbers can differ.

In Ark, your spendable balance is not a single on-chain UTXO in your wallet. It is a set of Virtual Transaction Outputs (VTXOs): off-chain outputs created inside the operator’s periodic batch transactions. Each batch builds a Virtual Transaction Tree (VTXT) whose leaves are individual VTXOs assigned to participants.

A VTXO behaves like a UTXO you can spend, but it lives in Ark’s shared batch structure until you exit, refresh, or the operator sweeps expired batches. Wavelength tracks every live VTXO the daemon knows about and uses them as inputs when you send, swap, or leave.

Each VTXO carries two spend paths:

  • Collaborative path: you and the Ark operator co-sign, so the output can move instantly inside Ark (for example through a cooperative leave or an out-of-round transfer).
  • Unilateral exit path: you alone can broadcast after a relative timelock (CSV delay) if the operator stops cooperating.

VTXOs also have a sweep delay tied to their batch. Before the operator becomes eligible to sweep the batch, the VTXO should either be refreshed through a batch swap (getting a fresh VTXO in a newer batch) or moved to on-chain Bitcoin. This refresh is daemon-driven: Wavelength detects the approaching expiry and submits the batch swap for you, so there is no method to call yourself. Wavelength surfaces expiry pressure through balance and activity updates rather than asking you to manage raw outpoints manually.

Confirmed vs pending

When you show balance in a UI, users usually want one “available” number. Wavelength splits the picture into confirmed and pending fields so you can explain why spendable funds lag behind what they see on-chain or in the activity feed.

balance() returns three satoshi fields that answer different questions:

  • confirmedSat: funds you can treat as settled inside the wallet. These VTXOs are live, unencumbered, and available for send, swap, or leave.
  • pendingInSat: inbound value still moving through boarding, a Lightning swap, or a round that has not finished yet. A deposit may be visible on-chain before it becomes a VTXO; a Lightning receive stays pending until the swap server funds and you claim the virtual HTLC (vHTLC) (see Lightning payments are swaps).
  • pendingOutSat: outbound value reserved by in-flight sends, swap funding, or leave requests. The wallet holds these amounts aside so you do not double-spend the same VTXOs while an operation is still settling.

Confirmed satoshis are what you show as “available.” Pending fields explain why the total on-screen balance can differ from what a user can spend right now, and why a payment can appear in the activity feed before it affects confirmed balance.

Pair balance() with the activity stream when you need per-operation detail: each Entry carries its own status (pending, complete, or failed) and a coarse phase (for example waiting_for_payment, settling, or confirmed) that describes where that specific operation sits in its pipeline. See the wavelength-core reference for the full Balance and Entry shapes.

Boarding → rounds → VTXO lifecycle

On-chain Bitcoin enters Ark through boarding:

  1. deposit() returns a boarding address (and an initial deposit activity entry). Send on-chain BTC to that address like any other receive address.
  2. The operator watches the chain via Esplora. Once the deposit confirms and meets the operator’s policy, the wallet submits a boarding request for the next round.
  3. Rounds advance on a fixed cadence (often around one minute on public test networks; roughly 90 seconds end-to-end is a reasonable UX expectation). During request collection the operator aggregates boarding inputs, VTXO creations, leaves, and batch swaps into one batch transaction.
  4. When the round completes, your boarded sats become one or more VTXOs in the new batch. The deposit entry moves from pending toward confirmed, and confirmed balance increases.

Every VTXO you receive this way inherits the batch’s CSV expiry (sweep delay). As that horizon approaches, the VTXO is refreshed with a batch swap: forfeiting the old VTXO in a round in exchange for a fresh one in a newer batch, which resets the expiry clock. Wavelength handles the round interaction; you do not pick batch transaction IDs yourself. Operators can advertise a free refresh window: a late-lifetime span of blocks in which a pure refresh gets its fee waived. When that waiver can be reached without weakening exit safety, the daemon delays the automatic refresh into the window; the advertised width surfaces as serverInfo.freeRefreshWindowBlocks on getInfo(). See Leaving Ark for the exit() / getExitPlan() / sweepWallet() APIs that apply if you choose to move funds on-chain instead of refreshing.

Lightning receives and sends also end in VTXOs (via vHTLC outputs), but on-chain boarding is the path most integrators hit first: address → wait for round → live VTXO → refresh before expiry.