Skip to main content

Overview

Ark supports multiple payment methods from a single offchain balance:
  • Arkoor: Instant offchain payments to other Ark addresses
  • Onchain: Send to Bitcoin addresses (offboarding)
  • Lightning: Pay BOLT11/BOLT12 invoices and Lightning addresses

Arkoor Payments

Arkoor (Ark out-of-round) enables instant payments without waiting for a round.

Sending to an Ark Address

use bitcoin::Amount;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Generate or receive an Ark address
    let address = ark::Address::from_str(
        "ark1..." // Ark address from recipient
    )?;

    // Validate the address
    wallet.validate_arkoor_address(&address).await?;

    // Send payment
    let amount = Amount::from_sat(10_000);
    let vtxos = wallet.send_arkoor_payment(&address, amount).await?;

    println!("Payment sent! Created {} VTXOs", vtxos.len());
    for vtxo in vtxos {
        println!("VTXO: {}, amount: {}", vtxo.id(), vtxo.amount());
    }

    Ok(())
}

How Arkoor Works

1

VTXO selection

The wallet selects VTXOs to cover the payment amount.
2

Create arkoor package

Builds a transaction that creates new VTXOs for the recipient and change VTXOs for you.
3

Server cosigning

The Ark server cosigns the transaction to enable instant settlement.
4

Delivery

VTXOs are delivered to the recipient via their specified delivery method (typically server mailbox).

Address Validation

Always validate addresses before sending:
match wallet.validate_arkoor_address(&address).await {
    Ok(()) => println!("Address is valid"),
    Err(e) if e.to_string().contains("different server") => {
        println!("Address is for a different Ark server");
    }
    Err(e) if e.to_string().contains("No VTXO delivery") => {
        println!("Address missing delivery mechanism");
    }
    Err(e) => return Err(e),
}

Multiple VTXOs

If your wallet doesn’t have a single VTXO large enough to cover the payment, multiple payments will be chained together, resulting in the recipient receiving multiple VTXOs.
// Send 100,000 sats (may create multiple VTXOs)
let vtxos = wallet.send_arkoor_payment(&address, Amount::from_sat(100_000)).await?;

if vtxos.len() > 1 {
    println!("Payment split into {} VTXOs", vtxos.len());
}

Onchain Payments

Send from your offchain balance to a Bitcoin address (offboarding).

Send to Onchain Address

use bitcoin::Address;

// Parse destination address
let destination = Address::from_str("bc1q...")?;
let amount = Amount::from_sat(50_000);

// Send onchain
let txid = wallet.send_onchain(destination, amount).await?;
println!("Sent onchain: {}", txid);

Offboard Specific VTXOs

// Offboard specific VTXOs
let vtxos = wallet.spendable_vtxos().await?;
let destination = Address::from_str("bc1q...")?.assume_checked();

let txid = wallet.offboard_vtxos(
    vtxos.iter().take(2), // First 2 VTXOs
    destination
).await?;

println!("Offboarded to {}", txid);

Offboard All Funds

// Move entire Ark balance onchain
let destination = Address::from_str("bc1q...")?.assume_checked();
let txid = wallet.offboard_all(destination).await?;

println!("Offboarded everything: {}", txid);

Offboard Process

1

Create arkoor preparation

If your VTXOs don’t exactly match the offboard amount, an arkoor transaction first creates appropriately-sized VTXOs.
2

Prepare offboard

The wallet builds an offboard request and sends it to the server.
3

Sign forfeits

You sign forfeit transactions that allow the server to claim your VTXOs if the offboard completes.
4

Broadcast

The server co-signs and broadcasts the offboard transaction to the Bitcoin network.

Offboard Fees

Offboarding involves service fees plus onchain transaction fees:
use bark::FeeEstimate;

// Estimate offboard fees
let destination = Address::from_str("bc1q...")?.assume_checked();
let amount = Amount::from_sat(100_000);

let estimate = wallet.estimate_offboard(destination, amount).await?;

println!("Offboard amount: {}", estimate.gross_amount);
println!("Service fee: {}", estimate.fee);
println!("Net received: {}", estimate.net_amount);
println!("VTXOs used: {}", estimate.vtxos_used);

Checking Balance Before Sending

// Get current balance
let balance = wallet.balance().await?;

println!("Spendable: {}", balance.spendable);
println!("Pending in round: {}", balance.pending_in_round);
println!("Pending exit: {:?}", balance.pending_exit);

// Ensure sufficient funds
let amount = Amount::from_sat(10_000);
if balance.spendable < amount {
    return Err(anyhow!("Insufficient balance"));
}

VTXO Selection

The wallet automatically selects VTXOs to cover payments:
// Manually select VTXOs (advanced)
let amount = Amount::from_sat(50_000);
let vtxos = wallet.select_vtxos_to_cover(amount).await?;

for vtxo in &vtxos {
    println!("Selected VTXO {}: {}", vtxo.id(), vtxo.amount());
}

let total: Amount = vtxos.iter().map(|v| v.amount()).sum();
println!("Total selected: {}", total);

Selection with Fees

// Select VTXOs accounting for fees
let amount = Amount::from_sat(50_000);
let (vtxos, fee) = wallet.select_vtxos_to_cover_with_fee(
    amount,
    |amt, vtxo_list| {
        // Custom fee calculation
        Ok(Amount::from_sat(100)) // Example fee
    }
).await?;

println!("Selected {} VTXOs with fee {}", vtxos.len(), fee);

Error Handling

match wallet.send_arkoor_payment(&address, amount).await {
    Ok(vtxos) => println!("Sent successfully"),
    Err(e) if e.to_string().contains("enough suitable VTXOs") => {
        println!("Insufficient balance");
    }
    Err(e) if e.to_string().contains("deliver") => {
        println!("Failed to deliver to recipient");
    }
    Err(e) => {
        println!("Payment failed: {:#}", e);
        return Err(e);
    }
}

Transaction History

Track your payment history:
use bark::movement::Movement;

// Get all movements (newest first)
let movements = wallet.history().await?;

for movement in movements {
    println!("Type: {}", movement.kind);
    println!("Status: {:?}", movement.status);
    println!("Amount: {}", movement.effective_balance);
    println!("Fee: {}", movement.fee);
    println!("Time: {:?}", movement.timestamp);
}

Best Practices

1

Validate addresses

Always validate Ark addresses before sending:
wallet.validate_arkoor_address(&address).await?;
2

Check balance

Ensure sufficient spendable balance before initiating payments.
3

Handle change VTXOs

Arkoor payments create change VTXOs. Periodically refresh them to prevent dust accumulation:
// Refresh VTXOs during maintenance
wallet.maintenance_refresh().await?;
4

Monitor movements

Use the movements API to track payment status and troubleshoot issues.
Offboarding is more expensive than arkoor payments. Use arkoor whenever the recipient has an Ark address.

Next Steps

Lightning Payments

Pay Lightning invoices from your Ark balance

Receiving Payments

Generate addresses to receive payments

Build docs developers (and LLMs) love