Skip to main content

Omnia VTXO — Virtual UTXO Claim Layer

@totemsdk/omnia-vtxo is a cash-like off-chain balance primitive that gives Totem Edge applications a way to hold, transfer, split, merge, and exit token balances without requiring a live Minima node for every operation.

Each VTXO (Virtual Transaction Output) is a claim on a pool of on-chain capacity, backed by a deterministic Merkle commitment tree. Think of it as a bearer note inside a shared liquidity pool: the pool operator holds the on-chain UTXO; the holder holds the VTXO proof.


How VTXOs differ from Statechains

Both VTXOs and Statechains are off-chain ownership primitives, but they are designed for different use cases:

StatechainOmnia VTXO
Ownership transferBlind SE co-signaturePure functional, no live signing needed
Privacy modelBlind Mercury SELocal commitment root
SplittingNot supportedSupported natively
MergingNot supportedSupported natively
Exit modelOn-chain UTXO handoffPool epoch settlement
Use casePrivacy UTXO custodyMerchant receipts, credits, pool liquidity

The pool model

A pool is the on-chain anchor that backs a set of VTXOs. Pools are created by an operator and hold a fixed totalCapacity. Minting a VTXO reduces the pool's availableCapacity. Exiting a VTXO returns capacity to the chain.

import { createPool } from '@totemsdk/omnia-vtxo';

const pool = createPool({
operator: 'MxOperatorAddress',
tokenId: '0x00', // Minima native token
totalCapacity: BigInt(1_000_000_000),
nonce: 'pool-genesis-v1', // makes poolId deterministic
}, Date.now());
// pool.poolId is SHA3-256(operator + tokenId + nonce) — always deterministic
FieldDescription
poolIdDeterministic hash — never changes
operatorMinima address holding the on-chain backing UTXO
tokenIdToken this pool holds
totalCapacityTotal token units the pool can back
availableCapacityRemaining mintable capacity
epochAdvances on each batch refresh round
commitmentRootCurrent Merkle root over all live VTXOs

Full operation lifecycle

1. Mint

Creates a new VTXO, reducing pool.availableCapacity.

import { mintVtxo } from '@totemsdk/omnia-vtxo';

const { pool: p1, vtxo: note, receipt } = mintVtxo(
pool,
{ owner: 'MxAlice', amount: BigInt(100_000), nonce: 'note-1' },
Date.now(),
);
// note.status === 'active'

2. Transfer

Moves a VTXO to a new owner. Full transfer marks the input as transferred and creates a new active output.

import { transferVtxo } from '@totemsdk/omnia-vtxo';

const { input: spent, output: bobNote, transfer } = transferVtxo(
note,
{ recipient: 'MxBob', amount: BigInt(100_000), nonce: 'tx-1' },
Date.now(),
);
// spent.status === 'transferred', bobNote.status === 'active'

3. Split

Divides one VTXO into two or more pieces. The input becomes split; all outputs are active.

import { splitVtxo } from '@totemsdk/omnia-vtxo';

const { input: splitInput, outputs: [piece1, piece2] } = splitVtxo(
bobNote,
{ amounts: [BigInt(60_000), BigInt(40_000)], nonces: ['sp-1', 'sp-2'] },
Date.now(),
);
// piece1.amount + piece2.amount must equal bobNote.amount

4. Merge

Combines multiple VTXOs into one. Inputs become merged; the output is active. All inputs must belong to the same pool and token.

import { mergeVtxos } from '@totemsdk/omnia-vtxo';

const { output: mergedNote } = mergeVtxos(
[piece1, piece2],
{ nonce: 'mg-1', owner: 'MxBob' },
Date.now(),
);

5. Refresh

Re-anchors a VTXO against a new pool epoch. The old VTXO becomes refreshed; the new one is active with a higher epoch number.

import { refreshVtxo } from '@totemsdk/omnia-vtxo';

const { old: staleNote, refreshed: freshNote } = refreshVtxo(
mergedNote,
{ newEpoch: 1, nonce: 'ref-1' },
Date.now(),
);

6. Exit

Initiates and finalises a unilateral exit, returning the VTXO's value to the on-chain pool.

import { createExitDraft, markExiting, markExited } from '@totemsdk/omnia-vtxo';

const { draft, receipt: exitReceipt } = createExitDraft(freshNote, Date.now());
// draft.draftType === 'mock-exit' (see Security Notes below)

VtxoStatus state machine

Every VTXO moves through the following states. Terminal states are transferred, split, merged, refreshed, spent, and exited.

┌──────── mint ────────┐
▼ │
active ──transfer──► transferred

├─ partial-transfer ──► split (input)
│ │
├─ split ────────────► split (input)

├─ merge ────────────► merged (inputs)

├─ refresh ──────────► refreshed (old)

├─ exit_initiated ───► exiting
│ │
│ exited
└─ spent ──────────── spent

Verification and conservation

import { verifyVtxo, verifyConservation } from '@totemsdk/omnia-vtxo';

// Verify a single VTXO's proof fields
const check = verifyVtxo(freshNote);
console.log('Valid:', check.valid);

// Verify balance conservation across an operation
const conservation = verifyConservation({
inputs: [note],
outputs: [bobNote], // sum(inputs) must equal sum(outputs)
});
console.log('Conservation holds:', conservation.valid);

Error hierarchy

All errors extend OmniaVtxoError and carry a structured code string:

OmniaVtxoError
├── VtxoAmountError — invalid amounts (zero, overflow, sum mismatch)
├── VtxoStatusError — wrong status for the requested operation
├── VtxoOwnershipError — owner mismatch
├── VtxoProofError — Merkle proof verification failure
├── VtxoPoolCapacityError — insufficient pool capacity
├── VtxoPolicyError — policy violation (min/max amounts, epoch ordering)
├── VtxoMergeError — merge precondition failure (cross-pool, duplicates)
├── VtxoSplitError — split precondition failure
└── VtxoExitError — exit precondition failure

Persistence

OmniaVtxoStore is a simple async interface. MemoryOmniaVtxoStore ships as the in-memory MVP implementation.

import { MemoryOmniaVtxoStore } from '@totemsdk/omnia-vtxo';

const store = new MemoryOmniaVtxoStore();
await store.savePool(pool);
await store.saveVtxo(freshNote);
const loaded = await store.getVtxo(freshNote.vtxoId);
const all = await store.listVtxos(pool.poolId);

Serialization

serializeVtxo / deserializeVtxo produce BigInt-safe JSON strings (BigInt values are encoded as "__bigint__:<value>").

import { serializeVtxo, deserializeVtxo, serializePool, deserializePool } from '@totemsdk/omnia-vtxo';

const json = serializeVtxo(freshNote);
const restored = deserializeVtxo(json);

Security notes / MVP caveats

This is an MVP SDK — not a production Ark implementation.

Several components are stubs awaiting production hardening:

  • Operator receipts use MOCK_OPERATOR_SIGNATURE. In production the operator signs over the commitment root with a real WOTS or Schnorr key, and clients must verify against the operator's published public key.
  • Commitment roots are single-batch local roots, not pool-wide epoch roots. A full deployment requires the operator to broadcast a pool-wide root each epoch.
  • Exit scripts use draftType: 'mock-exit'. Real exits require audited KISSVM covenant exit scripts and a watchtower monitoring the on-chain dispute window.
  • No watchtower / dispute monitoring. Operator equivocation is not detected.
  • No replay protection beyond nonce uniqueness. Nonces must be globally unique per pool.

Future roadmap

  • Real Omnia factory backing — pool funds held in an N-of-N Omnia factory UTXO with batch settlement on exit.
  • Batch refresh rounds — operator posts a new commitment root; all live VTXOs refreshed in a single Minima transaction.
  • Operator signing — real WOTS receipts replacing MOCK_OPERATOR_SIGNATURE, with client-side signature verification.
  • Wallet integration — direct integration with Totem Wallet's coin selection and WOTS signing infrastructure.
  • Connect API methodstotem_mintVtxo, totem_transferVtxo, totem_listVtxos via the TOTEM_CONNECT dApp API.
  • Persistence adapters — PostgreSQL and IndexedDB adapters for OmniaVtxoStore.
  • KISSVM exit script — audited on-chain exit covenant for unilateral exit with timelock enforcement.

See also