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
VTXO selection
The wallet selects VTXOs to cover the payment amount.
Create arkoor package
Builds a transaction that creates new VTXOs for the recipient and change VTXOs for you.
Server cosigning
The Ark server cosigns the transaction to enable instant settlement.
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
Create arkoor preparation
If your VTXOs don’t exactly match the offboard amount, an arkoor transaction first creates appropriately-sized VTXOs.
Prepare offboard
The wallet builds an offboard request and sends it to the server.
Sign forfeits
You sign forfeit transactions that allow the server to claim your VTXOs if the offboard completes.
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
Validate addresses
Always validate Ark addresses before sending: wallet . validate_arkoor_address ( & address ) . await ? ;
Check balance
Ensure sufficient spendable balance before initiating payments.
Handle change VTXOs
Arkoor payments create change VTXOs. Periodically refresh them to prevent dust accumulation: // Refresh VTXOs during maintenance
wallet . maintenance_refresh () . await ? ;
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