Sending an on-chain transaction (Swap-Out)

You can send funds from the Breez SDK wallet to an on-chain address as follows.

Setting the fee

First, fetch the current reverse swap fees:

Rust
let current_fees = sdk
    .fetch_reverse_swap_fees(ReverseSwapFeesRequest {
        send_amount_sat: Some(50_000),
        claim_tx_feerate: None,
    })
    .await?;

info!(
    "Total estimated fees for reverse swap: {:?}",
    current_fees.total_fees
);
Swift
let sendAmountSat: UInt64 = 50_000
let currentFees = try? sdk.fetchReverseSwapFees(req: ReverseSwapFeesRequest(sendAmountSat: sendAmountSat))
print("Total estimated fees for reverse swap: \(String(describing: currentFees?.totalFees))")
Kotlin
try {
    val fees = sdk.fetchReverseSwapFees(ReverseSwapFeesRequest(50_000_u))
    // Log.v("Breez", "Total estimated fees for reverse swap: ${fees.totalFees}")
} catch (e: Exception) {
    // handle error
}
React Native
try {
  const currentFees = await fetchReverseSwapFees({ sendAmountSat: 50000 })

  console.log(
    `Total estimated fees for reverse swap: ${currentFees.totalFees}`
  )
} catch (err) {
  console.error(err)
}
Dart
ReverseSwapFeesRequest req = const ReverseSwapFeesRequest(sendAmountSat: 50000);
ReverseSwapPairInfo currentFees = await BreezSDK().fetchReverseSwapFees(req: req);
print("Total estimated fees for reverse swap: ${currentFees.totalFees}");
Python
req = breez_sdk.ReverseSwapFeesRequest(send_amount_sat=50000)
current_fees = sdk_services.fetch_reverse_swap_fees(req)
print("Total estimated fees for reverse swap: ", current_fees.total_fees)
Go
sendAmountSat := uint64(50_000)
reverseSwapFeesRequest := breez_sdk.ReverseSwapFeesRequest{
    SendAmountSat: &sendAmountSat,
}
if currentFees, err := sdk.FetchReverseSwapFees(reverseSwapFeesRequest); err == nil {
    log.Printf("Total estimated fees for reverse swap: %v", currentFees.TotalFees)
}
C#
try
{
    var currentFees = sdk.FetchReverseSwapFees(
        new ReverseSwapFeesRequest(50000));
    Console.WriteLine(
        $"Total estimated fees for reverse " +
        $"swap: {currentFees.totalFees}");
}
catch (Exception)
{
    // Handle error
}

Developer note

The reverse swap will involve two on-chain transactions, for which the mining fees can only be estimated. They will happen automatically once the process is started, but the last two values above are these estimates to help you get a picture of the total costs.

Fetching the fees also tells you what is the range of amounts the service allows:

Rust
info!("Minimum amount, in sats: {}", current_fees.min);
info!("Maximum amount, in sats: {}", current_fees.max);
Swift
print("Minimum amount, in sats: \(currentFees.min)")
print("Maximum amount, in sats: \(currentFees.max)")
Kotlin
// Log.v("Breez", "Minimum amount, in sats: ${fees.min}")
// Log.v("Breez", "Maximum amount, in sats: ${fees.max}")
React Native
console.log(`Minimum amount, in sats: ${currentFees.min}`)
console.log(`Maximum amount, in sats: ${currentFees.max}`)
Dart
print("Minimum amount, in sats: ${currentFees.min}");
print("Maximum amount, in sats: ${currentFees.max}");
Python
print("Minimum amount, in sats: ", current_fees.min)
print("Maximum amount, in sats: ", current_fees.max)
Go
log.Printf("Minimum amount, in sats: %v", currentFees.Min)
log.Printf("Maximum amount, in sats: %v", currentFees.Max)
C#
Console.WriteLine($"Minimum amount, in sats: {currentFees.min}");
Console.WriteLine($"Maximum amount, in sats: {currentFees.max}");

Sending all funds

In case you want to drain your channels you need to know the maximum sendable amount to an on-chain address:

Rust
let max_amount = sdk.max_reverse_swap_amount().await?;

info!("Max reverse swap amount: {:?}", max_amount.total_sat);
Swift
let maxAmount = try? sdk.maxReverseSwapAmount()
print("Max reverse swap amount: \(String(describing: maxAmount?.totalSat))")
Kotlin
try {
    val maxAmount = sdk.maxReverseSwapAmount()
    // Log.v("Breez", "Max reverse swap amount: ${maxAmount.totalSat}")
} catch (e: Exception) {
    // handle error
}
React Native
try {
  const maxAmount = await maxReverseSwapAmount()

  console.log(
    `Max reverse swap amount: ${maxAmount.totalSat}`
  )
} catch (err) {
  console.error(err)
}
Dart
MaxReverseSwapAmountResponse maxAmount = await BreezSDK().maxReverseSwapAmount();
print("Max reverse swap amount: ${maxAmount.totalSat}");
Python
max_amount = sdk_services.max_reverse_swap_amount()
print("Max reverse swap amount: ", max_amount.totalSat)
Go
if maxAmount, err := sdk.MaxReverseSwapAmount(); err == nil {
    log.Printf("Max reverse swap amount: %v", maxAmount.TotalSat)
}
C#
try
{
    var maxAmountResponse = sdk.MaxReverseSwapAmount();
    Console.WriteLine(
        $"Max reverse swap amount {maxAmountResponse.totalSat}");
}
catch (Exception)
{
    // Handle error
}

Executing the Swap

Once you decided about the amount and checked the fees are acceptable, you can start the reverse swap:

Rust
let destination_address = String::from("bc1..");
let amount_sat = current_fees.min;
let sat_per_vbyte = fee_rate;

sdk.send_onchain(SendOnchainRequest {
    pair_hash: current_fees.fees_hash,
    amount_sat,
    sat_per_vbyte,
    onchain_recipient_address: destination_address,
})
.await?;
Swift
let destinationAddress = "bc1.."
let amountSat = currentFees.min
let satPerVbyte: UInt32 = 5

let response = try? sdk.sendOnchain(req: SendOnchainRequest(amountSat: amountSat, onchainRecipientAddress: destinationAddress, pairHash: currentFees.feesHash, satPerVbyte: satPerVbyte))
Kotlin
val address = "bc1.."
val amountSat = 123.toULong()
val satPerVbyte = 1.toUInt()
try {
    sdk.sendOnchain(SendOnchainRequest(amountSat, address, fees.feesHash, satPerVbyte))
} catch (e: Exception) {
    // handle error
}
React Native
try {
  const onchainRecipientAddress = 'bc1..'
  const amountSat = currentFees.min
  const satPerVbyte = 5

  const reverseSwapInfo = await sendOnchain({
    amountSat,
    onchainRecipientAddress,
    pairHash: currentFees.feesHash,
    satPerVbyte
  })
} catch (err) {
  console.error(err)
}
Dart
SendOnchainRequest req = SendOnchainRequest(
  amountSat: amountSat,
  onchainRecipientAddress: onchainRecipientAddress,
  pairHash: pairHash,
  satPerVbyte: satPerVbyte,
);
SendOnchainResponse resp = await BreezSDK().sendOnchain(req: req);
Python
destination_address = "bc1.."
amount_sat = 50000
sat_per_vbyte = fee_rate
try:
    req = breez_sdk.SendOnchainRequest(amount_sat, destination_address, current_fees.fees_hash, sat_per_vbyte)
    sdk_services.send_onchain(req)
Go
destinationAddress := "bc1.."
sendAmountSat := uint64(50_000)
satPerVbyte := uint32(5)
if currentFees, err := sdk.FetchReverseSwapFees(breez_sdk.ReverseSwapFeesRequest{SendAmountSat: &sendAmountSat}); err == nil {
    sendOnchainRequest := breez_sdk.SendOnchainRequest{
        AmountSat:               sendAmountSat,
        OnchainRecipientAddress: destinationAddress,
        PairHash:                currentFees.FeesHash,
        SatPerVbyte:             satPerVbyte,
    }
    if reverseSwapInfo, err := sdk.SendOnchain(sendOnchainRequest); err == nil {
        log.Printf("%#v", reverseSwapInfo)
    }
}
C#
var destinationAddress = "bc1..";
var amountSat = currentFees.min;
var satPerVbyte = feeRate;
try
{
    var reverseSwapInfo = sdk.SendOnchain(
        new SendOnchainRequest(
            amountSat,
            destinationAddress,
            currentFees.feesHash,
            satPerVbyte));
}
catch (Exception)
{
    // Handle error
}

Starting the reverse swap will trigger a HODL invoice payment, which will only be settled if the entire swap completes. This means you will see an outgoing pending payment in your list of payments, which locks those funds until the invoice is either settled or cancelled. This will happen automatically at the end of the reverse swap.

List in-progress Swaps

You can check its status with:

Rust
for rs in sdk.in_progress_reverse_swaps().await? {
    info!(
        "Reverse swap {} in progress, status is {:?}",
        rs.id, rs.status
    );
}
Swift
if let inProgressReverseSwaps = try? sdk.inProgressReverseSwaps() {
    for rs in inProgressReverseSwaps {
        print("Reverse swap \(rs.id) in progress, status is \(rs.status)")
    }
}
Kotlin
for (rs in sdk.inProgressReverseSwaps()) {
    // Log.v("Breez", "Reverse swap ${rs.id} in progress, status is ${rs.status}")
}
React Native
try {
  const swaps = await inProgressReverseSwaps()
  for (const swap of swaps) {
    console.log(
      `Reverse swap ${swap.id} in progress, status is ${swap.status}`
    )
  }
} catch (err) {
  console.error(err)
}
Dart
List<ReverseSwapInfo> inProgRevSwapList = await BreezSDK().inProgressReverseSwaps();
for (var inProgRevSwap in inProgRevSwapList) {
  print("Reverse swap ${inProgRevSwap.id} in progress, status is ${inProgRevSwap.status.name}");
}
Python
reverse_swaps = sdk_services.in_progress_reverse_swaps()
for rs in reverse_swaps:
    print("Reverse swap ",rs.id , " in progress, status is ", rs.status)
Go
if swaps, err := sdk.InProgressReverseSwaps(); err == nil {
    for _, swap := range swaps {
        log.Printf("Reverse swap %v in progress, status is %v", swap.Id, swap.Status)
    }
}
C#
try
{
    var swaps = sdk.InProgressReverseSwaps();
    foreach (var swap in swaps)
    {
        Console.WriteLine(
            $"Reverse swap {swap.id} in progress, " +
            $"status is {swap.status}`");
    }
}
catch (Exception)
{
    // Handle error
}

If the reverse swap is successful, you'll get the on-chain payment on your destination address and the HODL invoice will change from pending to settled.

If however something goes wrong at any point in the process, the initial HODL payment will be cancelled and the funds in your Breez SDK wallet will be unlocked.