Skip to main content
VTXO Management automates the renewal and recovery of virtual coins to prevent expiration and loss of funds. VtxoManager is also accessible from ServiceWorkerWallet via wallet.getVtxoManager().

Overview

VTXOs can be in different states and have an expiration time based on the batch expiry. The VtxoManager class provides automated management for:
  • Renewal: Renew VTXOs before they expire to maintain unilateral control of your funds
  • Recovery: Reclaim expired and swept VTXOs back to your wallet

Creating a VTXO Manager

import { MnemonicIdentity, VtxoManager, Wallet } from '@arkade-os/sdk'

const identity = MnemonicIdentity.fromMnemonic(mnemonic)
const wallet = await Wallet.create({
  identity,
  arkServerUrl: 'https://arkade.computer',
  settlementConfig: {
    vtxoThreshold: 259200, // 3 days, in seconds
    boardingUtxoSweep: true,
  },
})

// Use the wallet's settlementConfig when constructing a manager manually
const manager = new VtxoManager(wallet, undefined, wallet.settlementConfig)

// Or let the wallet create one lazily
const sameManager = await wallet.getVtxoManager()
renewalConfig with enabled and thresholdMs is deprecated. Prefer settlementConfig on the wallet, where vtxoThreshold is expressed in seconds.

VTXO Renewal

Renew VTXOs before they expire to keep your liquidity available. This settles all VTXOs (including recoverable ones) back to your wallet with a fresh expiration time.

Checking Expiring VTXOs

// Check which VTXOs are expiring soon
const expiringVtxos = await manager.getExpiringVtxos()

if (expiringVtxos.length > 0) {
  console.log(`${expiringVtxos.length} VTXOs expiring soon`)

  expiringVtxos.forEach(vtxo => {
    const timeLeft = vtxo.virtualStatus.batchExpiry! - Date.now()
    const hoursLeft = Math.floor(timeLeft / (1000 * 60 * 60))
    console.log(`VTXO ${vtxo.txid} expires in ${hoursLeft} hours`)
  })
}
  • timeLeft: remaining time until expiry (batchExpiry - current time) in milliseconds
  • hoursLeft: remaining time in hours, derived from timeLeft

Renewing VTXOs

// Renew all VTXOs to prevent expiration
const txid = await manager.renewVtxos()
console.log('Renewed:', txid)
Set up automatic renewal checks in your application to ensure VTXOs never expire. A good practice is to check daily and renew when the threshold is reached.

VTXO Recovery

Recover VTXOs that have been swept by the server or consolidate small amounts (sub-dust).

Checking Recoverable Balance

// Check what's recoverable
const balance = await manager.getRecoverableBalance()

console.log(`Recoverable: ${balance.recoverable} sats`)
console.log(`Subdust: ${balance.subdust} sats`)
console.log(`Subdust included: ${balance.includesSubdust}`)
console.log(`VTXO count: ${balance.vtxoCount}`)
Response shape:
type RecoverableBalance = {
  recoverable: bigint               // total recoverable amount in satoshis
  subdust: bigint                   // sub-dust amount included
  includesSubdust: boolean          // whether subdust is part of recoverable
  vtxoCount: number                 // number of recoverable VTXOs
}

Recovering VTXOs

if (balance.recoverable > 0n) {
  // Recover swept VTXOs and preconfirmed subdust
  const txid = await manager.recoverVtxos((event) => {
    console.log('Settlement event:', event.type)
  })
  console.log('Recovered:', txid)
}

Best Practices

Implement automatic renewal checks in your application:
// Check for expiring VTXOs daily
setInterval(async () => {
  const expiring = await manager.getExpiringVtxos()
  if (expiring.length > 0) {
    await manager.renewVtxos()
  }
}, 24 * 60 * 60 * 1000) // Once per day
Periodically check for recoverable funds:
// Check for recoverable VTXOs weekly
setInterval(async () => {
  const balance = await manager.getRecoverableBalance()
  if (balance.recoverable > 0n) {
    await manager.recoverVtxos()
  }
}, 7 * 24 * 60 * 60 * 1000) // Once per week
The threshold specifies how much time must remain before a VTXO is flagged for renewal. Adjust the threshold based on your needs:
  • 3 days (default): Good balance between proactive renewal and transaction costs
  • 1 day: More aggressive, suitable for high-value or frequently active wallets
  • 7 days: More relaxed, suitable for infrequently used wallets

Complete Example

Here’s a complete example of VTXO management:
import { Wallet, MnemonicIdentity, VtxoManager } from '@arkade-os/sdk'
import { LocalStorageAdapter } from '@arkade-os/sdk/adapters/localStorage'

// Setup wallet
const storage = new LocalStorageAdapter()
const mnemonic = await storage.getItem('mnemonic')
const identity = MnemonicIdentity.fromMnemonic(mnemonic)

const wallet = await Wallet.create({
  identity,
  arkServerUrl: 'https://arkade.computer',
  storage
})

// Create VTXO manager
const manager = new VtxoManager(wallet, undefined, {
  vtxoThreshold: 259200,
  boardingUtxoSweep: true,
})

// Check and renew expiring VTXOs
const expiringVtxos = await manager.getExpiringVtxos()
if (expiringVtxos.length > 0) {
  console.log(`Renewing ${expiringVtxos.length} expiring VTXOs...`)
  const renewTxid = await manager.renewVtxos()
  console.log('Renewal transaction:', renewTxid)
}

// Check and recover swept VTXOs
const balance = await manager.getRecoverableBalance()
if (balance.recoverable > 0n) {
  console.log(`Recovering ${balance.recoverable} sats...`)
  const recoverTxid = await manager.recoverVtxos((event) => {
    console.log('Settlement event:', event.type)
  })
  console.log('Recovery transaction:', recoverTxid)
}

Next Steps