Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Claiming deposits manually

A deposit the SDK does not claim on its own, usually because the claim costs more than the max claim fee, can be claimed with claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit. The recommended flow is to show the user the fee, ask for approval, then claim with a max fee that covers it.

Pricing a claim

fetch_claim_deposit_quotefetch_claim_deposit_quotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuoteFetchClaimDepositQuoteFetchClaimDepositQuote prices both ways of claiming a deposit: instantinstantinstantinstantinstantinstantinstantInstantInstant for an instant or expedited claim, and maturematurematurematurematurematurematureMatureMature for the standard claim. Each quote carries its fee and confirmations_requiredconfirmations_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequired, and the response carries the deposit's current confirmationsconfirmationsconfirmationsconfirmationsconfirmationsconfirmationsconfirmationsConfirmationsConfirmations. The early quote is requested from the provider on every call, so call it while the user is deciding, not on a timer.

Rust
let quote = sdk
    .fetch_claim_deposit_quote(FetchClaimDepositQuoteRequest {
        txid: deposit.txid.clone(),
        vout: deposit.vout,
    })
    .await?;

// The standard claim, and how many blocks away it is.
let blocks_to_wait = quote
    .mature
    .confirmations_required
    .saturating_sub(quote.confirmations);
info!(
    "Wait {} blocks and pay {} sats",
    blocks_to_wait, quote.mature.fee_sats
);

// An instant or expedited claim, when the provider offers one.
if let Some(instant) = &quote.instant {
    let blocks_to_wait = instant
        .confirmations_required
        .saturating_sub(quote.confirmations);
    info!(
        "Or wait {} blocks and pay {} sats",
        blocks_to_wait, instant.fee_sats
    );
}
Swift
let request = FetchClaimDepositQuoteRequest(txid: deposit.txid, vout: deposit.vout)
let quote = try await sdk.fetchClaimDepositQuote(request: request)

// The standard claim, and how many blocks away it is.
var blocksToWait: UInt32 = 0
if quote.mature.confirmationsRequired > quote.confirmations {
    blocksToWait = quote.mature.confirmationsRequired - quote.confirmations
}
print("Wait \(blocksToWait) blocks and pay \(quote.mature.feeSats) sats")

// An instant or expedited claim, when the provider offers one.
if let instant = quote.instant {
    var instantBlocks: UInt32 = 0
    if instant.confirmationsRequired > quote.confirmations {
        instantBlocks = instant.confirmationsRequired - quote.confirmations
    }
    print("Or wait \(instantBlocks) blocks and pay \(instant.feeSats) sats")
}
Kotlin
try {
    val quote = sdk.fetchClaimDepositQuote(
        FetchClaimDepositQuoteRequest(
            txid = deposit.txid,
            vout = deposit.vout
        )
    )

    // UInt subtraction wraps, so clamp the wait at zero.
    fun blocksToWait(required: UInt) =
        if (required > quote.confirmations) required - quote.confirmations else 0u

    // The standard claim, and how many blocks away it is.
    val matureWait = blocksToWait(quote.mature.confirmationsRequired)
    // Log.v("Breez", "Wait $matureWait blocks and pay ${quote.mature.feeSats} sats")

    // An instant or expedited claim, when the provider offers one.
    quote.instant?.let { instant ->
        val instantWait = blocksToWait(instant.confirmationsRequired)
        // Log.v("Breez", "Or wait $instantWait blocks and pay ${instant.feeSats} sats")
    }
} catch (e: Exception) {
    // handle error
}
C#
var request = new FetchClaimDepositQuoteRequest(
    txid: deposit.txid,
    vout: deposit.vout
);
var quote = await sdk.FetchClaimDepositQuote(request: request);

// The standard claim, and how many blocks away it is.
var blocksToWait = quote.mature.confirmationsRequired > quote.confirmations
    ? quote.mature.confirmationsRequired - quote.confirmations
    : 0U;
Console.WriteLine($"Wait {blocksToWait} blocks and pay {quote.mature.feeSats} sats");

// An instant or expedited claim, when the provider offers one.
if (quote.instant is ClaimDepositQuote instant)
{
    var instantBlocksToWait = instant.confirmationsRequired > quote.confirmations
        ? instant.confirmationsRequired - quote.confirmations
        : 0U;
    Console.WriteLine($"Or wait {instantBlocksToWait} blocks and " +
                      $"pay {instant.feeSats} sats");
}
Javascript
const quote = await sdk.fetchClaimDepositQuote({
  txid: deposit.txid,
  vout: deposit.vout
})

// The standard claim, and how many blocks away it is.
const blocksToWait = Math.max(
  0,
  quote.mature.confirmationsRequired - quote.confirmations
)
console.log(`Wait ${blocksToWait} blocks and pay ${quote.mature.feeSats} sats`)

// An instant or expedited claim, when the provider offers one.
if (quote.instant != null) {
  const instantBlocksToWait = Math.max(
    0,
    quote.instant.confirmationsRequired - quote.confirmations
  )
  console.log(
    `Or wait ${instantBlocksToWait} blocks and ` +
    `pay ${quote.instant.feeSats} sats`
  )
}
React Native
const quote = await sdk.fetchClaimDepositQuote({
  txid: deposit.txid,
  vout: deposit.vout
})

// The standard claim, and how many blocks away it is.
const blocksToWait = Math.max(0, quote.mature.confirmationsRequired - quote.confirmations)
console.log(`Wait ${blocksToWait} blocks and pay ${quote.mature.feeSats} sats`)

// An instant or expedited claim, when the provider offers one.
const instant = quote.instant
if (instant != null) {
  const instantBlocks = Math.max(0, instant.confirmationsRequired - quote.confirmations)
  console.log(`Or wait ${instantBlocks} blocks and pay ${instant.feeSats} sats`)
}
Flutter
final request = FetchClaimDepositQuoteRequest(
  txid: deposit.txid,
  vout: deposit.vout,
);
final quote = await sdk.fetchClaimDepositQuote(request: request);

// The standard claim, and how many blocks away it is.
final confirmations = quote.confirmations;
final matureBlocks = quote.mature.confirmationsRequired > confirmations
    ? quote.mature.confirmationsRequired - confirmations
    : 0;
print("Wait $matureBlocks blocks and pay ${quote.mature.feeSats} sats");

// An instant or expedited claim, when the provider offers one.
final instant = quote.instant;
if (instant != null) {
  final instantBlocks = instant.confirmationsRequired > confirmations
      ? instant.confirmationsRequired - confirmations
      : 0;
  print("Or wait $instantBlocks blocks and pay ${instant.feeSats} sats");
}
Python
try:
    request = FetchClaimDepositQuoteRequest(txid=deposit.txid, vout=deposit.vout)
    quote = await sdk.fetch_claim_deposit_quote(request=request)

    # The standard claim, and how many blocks away it is.
    blocks_to_wait = max(
        0, quote.mature.confirmations_required - quote.confirmations
    )
    logging.info(
        f"Wait {blocks_to_wait} blocks and pay {quote.mature.fee_sats} sats"
    )

    # An instant or expedited claim, when the provider offers one.
    if quote.instant is not None:
        blocks_to_wait = max(
            0, quote.instant.confirmations_required - quote.confirmations
        )
        logging.info(
            f"Or wait {blocks_to_wait} blocks and pay {quote.instant.fee_sats} sats"
        )
except Exception as error:
    logging.error(error)
    raise
Go
quote, err := sdk.FetchClaimDepositQuote(breez_sdk_spark.FetchClaimDepositQuoteRequest{
	Txid: deposit.Txid,
	Vout: deposit.Vout,
})
if err != nil {
	return err
}

// The standard claim, and how many blocks away it is.
blocksToWait := uint32(0)
if quote.Mature.ConfirmationsRequired > quote.Confirmations {
	blocksToWait = quote.Mature.ConfirmationsRequired - quote.Confirmations
}
log.Printf("Wait %v blocks and pay %v sats", blocksToWait, quote.Mature.FeeSats)

// An instant or expedited claim, when the provider offers one.
if quote.Instant != nil {
	instantBlocks := uint32(0)
	if quote.Instant.ConfirmationsRequired > quote.Confirmations {
		instantBlocks = quote.Instant.ConfirmationsRequired - quote.Confirmations
	}
	log.Printf("Or wait %v blocks and pay %v sats", instantBlocks, quote.Instant.FeeSats)
}
  • confirmations_requiredconfirmations_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequired is the depth the claim becomes available at, not the number of blocks left to wait. On the instantinstantinstantinstantinstantinstantinstantInstantInstant quote, 0 means an instant claim and 1 or 2 an expedited one.
  • The instantinstantinstantinstantinstantinstantinstantInstantInstant quote is absent when no early claim is offered right now: the provider will not front this deposit, the deposit has already reached the standard claim depth, the early claim would credit no sooner, or the provider could not be reached. Only the last one is worth retrying.
  • Each quote's max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats is the most that claim may cost for this deposit, under the max claim fee that applies to it. A fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats within it means the SDK makes that claim on its own. It is unset when automatic claiming is off.
  • The instantinstantinstantinstantinstantinstantinstantInstantInstant quote shows the provider's price, whether or not it is within its max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats.
  • The maturematurematurematurematurematurematureMatureMature quote is always present. It is flagged is_estimateis_estimateisEstimateisEstimateisEstimateisEstimateisEstimateIsEstimateIsEstimate when the provider will not quote the deposit yet, and its fee is then estimated from current on-chain fees.

What to offer depends on what the SDK would do anyway. Check the middle column first: where the SDK claims by itself, a dialog defaulting to the standard claim shows the user one outcome and delivers another.

QuoteWhat the SDK will doWhat to offer
No instantinstantinstantinstantinstantinstantinstantInstantInstant quoteStandard claimThe standard claim only
instantinstantinstantinstantinstantinstantinstantInstantInstant quote, depth not reached yetClaim early once the depth arrives, if fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats is within max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSatsThe standard claim, with the early claim shown as available in N blocks
instantinstantinstantinstantinstantinstantinstantInstantInstant quote at a reachable depth, fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats above max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSatsWait for the standard claimBoth, the early claim requiring a higher max fee
instantinstantinstantinstantinstantinstantinstantInstantInstant quote at a reachable depth, fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats within max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSatsClaim early by itselfThe early claim, or no choice at all

Claiming

Call claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit with a max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee that covers the fee the user approved. To claim early, it has to cover the instantinstantinstantinstantinstantinstantinstantInstantInstant quote's fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats. For a deposit whose automatic claim failed, the required fee is in its claim_errorclaim_errorclaimErrorclaimErrorclaimErrorclaimErrorclaimErrorClaimErrorClaimError:

Rust
if let Some(DepositClaimError::MaxDepositClaimFeeExceeded {
    required_fee_sats, ..
}) = &deposit.claim_error
{
    // Show UI to user with the required fee and get approval
    let user_approved = true; // Replace with actual user approval logic

    if user_approved {
        let request = ClaimDepositRequest {
            txid: deposit.txid.clone(),
            vout: deposit.vout,
            max_fee: Some(MaxFee {
                onchain: MaxOnchainFee::Fixed {
                    amount: *required_fee_sats,
                },
                proportional_ppm: None,
            }),
        };
        sdk.claim_deposit(request).await?;
    }
}
Swift
if case .maxDepositClaimFeeExceeded(_, _, _, let requiredFeeSats) = deposit.claimError {
    // Show UI to user with the required fee and get approval
    let userApproved = true  // Replace with actual user approval logic

    if userApproved {
        let claimRequest = ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: MaxFee(
                onchain: MaxOnchainFee.fixed(amount: requiredFeeSats),
                proportionalPpm: nil
            )
        )
        try await sdk.claimDeposit(request: claimRequest)
    }
}
Kotlin
try {
    val claimError = deposit.claimError
    if (claimError is DepositClaimError.MaxDepositClaimFeeExceeded) {
        val requiredFee = claimError.requiredFeeSats

        // Show UI to user with the required fee and get approval
        val userApproved = true // Replace with actual user approval logic

        if (userApproved) {
            val claimRequest = ClaimDepositRequest(
                txid = deposit.txid,
                vout = deposit.vout,
                maxFee = MaxFee(
                    onchain = MaxOnchainFee.Fixed(requiredFee),
                    proportionalPpm = null
                )
            )
            sdk.claimDeposit(claimRequest)
        }
    }
} catch (e: Exception) {
    // handle error
}
C#
if (deposit.claimError is DepositClaimError.MaxDepositClaimFeeExceeded exceeded)
{
    var requiredFee = exceeded.requiredFeeSats;

    // Show UI to user with the required fee and get approval
    var userApproved = true; // Replace with actual user approval logic

    if (userApproved)
    {
        var claimRequest = new ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: new MaxFee(
                onchain: new MaxOnchainFee.Fixed(amount: requiredFee),
                proportionalPpm: null
            )
        );
        await sdk.ClaimDeposit(request: claimRequest);
    }
}
Javascript
if (deposit.claimError?.type === 'maxDepositClaimFeeExceeded') {
  const requiredFee = deposit.claimError.requiredFeeSats

  // Show UI to user with the required fee and get approval
  const userApproved = true // Replace with actual user approval logic

  if (userApproved) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: { onchain: { type: 'fixed', amount: requiredFee } }
    }
    await sdk.claimDeposit(claimRequest)
  }
}
React Native
if (deposit.claimError?.tag === DepositClaimError_Tags.MaxDepositClaimFeeExceeded) {
  const requiredFee = deposit.claimError.inner.requiredFeeSats

  // Show UI to user with the required fee and get approval
  const userApproved = true // Replace with actual user approval logic

  if (userApproved) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee.create({
        onchain: new MaxOnchainFee.Fixed({ amount: requiredFee }),
        proportionalPpm: undefined
      })
    }
    await sdk.claimDeposit(claimRequest)
  }
}
Flutter
final claimError = deposit.claimError;
if (claimError is DepositClaimError_MaxDepositClaimFeeExceeded) {
  final requiredFee = claimError.requiredFeeSats;

  // Show UI to user with the required fee and get approval
  bool userApproved = true; // Replace with actual user approval logic

  if (userApproved) {
    final claimRequest = ClaimDepositRequest(
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee(
        onchain: MaxOnchainFee.fixed(amount: requiredFee),
        proportionalPpm: null,
      ),
    );
    await sdk.claimDeposit(request: claimRequest);
  }
}
Python
try:
    if isinstance(
        deposit.claim_error, DepositClaimError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDED
    ):
        required_fee = deposit.claim_error.required_fee_sats

        # Show UI to user with the required fee and get approval
        user_approved = True  # Replace with actual user approval logic

        if user_approved:
            claim_request = ClaimDepositRequest(
                txid=deposit.txid,
                vout=deposit.vout,
                max_fee=MaxFee(
                    onchain=MaxOnchainFee.FIXED(amount=required_fee),
                    proportional_ppm=None,
                ),
            )
            await sdk.claim_deposit(request=claim_request)
except Exception as error:
    logging.error(error)
    raise
Go
if claimErr := *deposit.ClaimError; claimErr != nil {
	if exceeded, ok := claimErr.(breez_sdk_spark.DepositClaimErrorMaxDepositClaimFeeExceeded); ok {
		requiredFee := exceeded.RequiredFeeSats

		// Show UI to user with the required fee and get approval
		userApproved := true // Replace with actual user approval logic

		if userApproved {
			maxFee := breez_sdk_spark.MaxFee{
				Onchain: breez_sdk_spark.MaxOnchainFeeFixed{Amount: requiredFee},
			}
			claimRequest := breez_sdk_spark.ClaimDepositRequest{
				Txid:   deposit.Txid,
				Vout:   deposit.Vout,
				MaxFee: &maxFee,
			}
			_, err := sdk.ClaimDeposit(claimRequest)
			if err != nil {
				var sdkErr *breez_sdk_spark.SdkError
				if errors.As(err, &sdkErr) {
					// Handle SdkError - can inspect specific variants if needed
					// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
				}
				return err
			}
		}
	}
}

Claiming a deposit that already has a claim returns SdkError::DepositClaimInProgressSdkError.DEPOSIT_CLAIM_IN_PROGRESSSdkError.depositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkError.DepositClaimInProgressSdkErrorDepositClaimInProgressSdkError.DepositClaimInProgress: either a claim is still running, or one has credited the deposit and is waiting for the provider to spend the output. Neither needs any action, and instant_claim_statusinstant_claim_statusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusinstantClaimStatusInstantClaimStatusInstantClaimStatus tells them apart.

Handling the outcome

claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit reports what it did in outcomeoutcomeoutcomeoutcomeoutcomeoutcomeoutcomeOutcomeOutcome:

OutcomeMeaning
ClaimDepositOutcome::SettledClaimDepositOutcome.SETTLEDClaimDepositOutcome.settledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcomeSettledClaimDepositOutcome.SettledA standard claim settled. Carries the resulting payment.
ClaimDepositOutcome::SubmittedClaimDepositOutcome.SUBMITTEDClaimDepositOutcome.submittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcomeSubmittedClaimDepositOutcome.SubmittedAn instant or expedited claim is settling. The payment arrives through list_paymentslist_paymentslistPaymentslistPaymentslistPaymentslistPaymentslistPaymentsListPaymentsListPayments and the payment events.
ClaimDepositOutcome::DeferredClaimDepositOutcome.DEFERREDClaimDepositOutcome.deferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcome.DeferredClaimDepositOutcomeDeferredClaimDepositOutcome.DeferredNothing was claimed yet. Its reasonreasonreasonreasonreasonreasonreasonReasonReason says what happens next.

A deferred claim's reasonreasonreasonreasonreasonreasonreasonReasonReason is one of:

  • ClaimDeferredReason::NoEarlyClaimAvailableClaimDeferredReason.NO_EARLY_CLAIM_AVAILABLEClaimDeferredReason.noEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailableClaimDeferredReasonNoEarlyClaimAvailableClaimDeferredReason.NoEarlyClaimAvailable: the provider will not front the deposit at its current depth. This usually clears with the next confirmation, and the SDK retries on its own.
  • ClaimDeferredReason::MaxFeeExceededClaimDeferredReason.MAX_FEE_EXCEEDEDClaimDeferredReason.maxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReason.MaxFeeExceededClaimDeferredReasonMaxFeeExceededClaimDeferredReason.MaxFeeExceeded: the early claim costs more than the max fee. The deposit waits for the standard claim unless the max fee is raised.
  • ClaimDeferredReason::ProviderDeclinedClaimDeferredReason.PROVIDER_DECLINEDClaimDeferredReason.providerDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReason.ProviderDeclinedClaimDeferredReasonProviderDeclinedClaimDeferredReason.ProviderDeclined: the provider refused or could not be reached. Another confirmation does not change this.

Only the first is a wait you can put a time on, so don't show "claimed in about 30 minutes" for the others.

The call can also fail with:

  • SdkError::MaxDepositClaimFeeExceededSdkError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDEDSdkError.maxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceededSdkErrorMaxDepositClaimFeeExceededSdkError.MaxDepositClaimFeeExceeded: the deposit has reached the standard claim depth and its claim costs more than the max fee. Nothing claims it until the max fee rises or on-chain fees fall.
  • SdkError::DepositTooSmallSdkError.DEPOSIT_TOO_SMALLSdkError.depositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkError.DepositTooSmallSdkErrorDepositTooSmallSdkError.DepositTooSmall: what would be left after the claim fee is below the dust limit. No max fee helps, but the deposit may become claimable once on-chain fees fall. fetch_claim_deposit_quotefetch_claim_deposit_quotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuoteFetchClaimDepositQuoteFetchClaimDepositQuote returns it too, and automatic claims record it in claim_errorclaim_errorclaimErrorclaimErrorclaimErrorclaimErrorclaimErrorClaimErrorClaimError.

Giving one deposit its own max fee

The max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee passed to claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit is recorded on the deposit, and the SDK's later automatic attempts on it use it too. This lets one deposit be treated differently without changing the configuration:

  • Above the instant claim fee, it lets that deposit be claimed early. The SDK keeps retrying, so there is no need to call claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit again.
  • Below the instant claim fee, it holds that deposit back from an early claim while the others still claim early.

The standard claim runs under the larger of the deposit's own max fee and the configured one. A raised max fee therefore covers the standard claim too, while a lowered one only restricts the early claim.

  • Calling claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit without a max_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFee claims under the configured max claim fee and clears the deposit's own.
  • The max fee is recorded before the claim is attempted, so it stands whatever the outcome.
  • It is readable as max_claim_feemax_claim_feemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeeMaxClaimFeeMaxClaimFee on the deposit, unset while the configured max claim fee applies.
  • It is kept on this device only. Another device sharing the wallet applies its own configuration to the same deposit.

Writing your own claim logic

For rules the max claim fee cannot express, such as claiming only above an amount, at certain times, or under a fee limit capped in sats, claim with claim_depositclaim_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDeposit yourself. Keep automatic claiming on and handle only the deposits it leaves unclaimed, or unset the max claim fee to decide every claim.

A deposit whose claim exceeded its max fee carries the total the claim would cost, required_fee_satsrequired_fee_satsrequiredFeeSatsrequiredFeeSatsrequiredFeeSatsrequiredFeeSatsrequiredFeeSatsRequiredFeeSatsRequiredFeeSats, in its claim_errorclaim_errorclaimErrorclaimErrorclaimErrorclaimErrorclaimErrorClaimErrorClaimError. This example accepts up to 1% of the deposit, but never more than 5,000 sats:

Rust
if let Some(DepositClaimError::MaxDepositClaimFeeExceeded {
    required_fee_sats, ..
}) = &deposit.claim_error
{
    // Accept up to 1% of the deposit amount, but never more than 5,000 sats
    let max_fee_sats = (deposit.amount_sats / 100).min(5_000);
    if *required_fee_sats <= max_fee_sats {
        let request = ClaimDepositRequest {
            txid: deposit.txid.clone(),
            vout: deposit.vout,
            max_fee: Some(MaxFee {
                onchain: MaxOnchainFee::Fixed {
                    amount: *required_fee_sats,
                },
                proportional_ppm: None,
            }),
        };
        sdk.claim_deposit(request).await?;
    }
}
Swift
if case .maxDepositClaimFeeExceeded(_, _, _, let requiredFeeSats) = deposit.claimError {
    // Accept up to 1% of the deposit amount, but never more than 5,000 sats
    let maxFeeSats = min(deposit.amountSats / 100, 5_000)
    if requiredFeeSats <= maxFeeSats {
        let claimRequest = ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: MaxFee(
                onchain: MaxOnchainFee.fixed(amount: requiredFeeSats),
                proportionalPpm: nil
            )
        )
        try await sdk.claimDeposit(request: claimRequest)
    }
}
Kotlin
try {
    val claimError = deposit.claimError
    if (claimError is DepositClaimError.MaxDepositClaimFeeExceeded) {
        val requiredFee = claimError.requiredFeeSats

        // Accept up to 1% of the deposit amount, but never more than 5,000 sats
        val maxFeeSats = minOf(deposit.amountSats / 100uL, 5_000uL)
        if (requiredFee <= maxFeeSats) {
            val claimRequest = ClaimDepositRequest(
                txid = deposit.txid,
                vout = deposit.vout,
                maxFee = MaxFee(
                    onchain = MaxOnchainFee.Fixed(requiredFee),
                    proportionalPpm = null
                )
            )
            sdk.claimDeposit(claimRequest)
        }
    }
} catch (e: Exception) {
    // handle error
}
C#
if (deposit.claimError is DepositClaimError.MaxDepositClaimFeeExceeded exceeded)
{
    var requiredFee = exceeded.requiredFeeSats;

    // Accept up to 1% of the deposit amount, but never more than 5,000 sats
    var maxFeeSats = Math.Min(deposit.amountSats / 100, 5_000UL);
    if (requiredFee <= maxFeeSats)
    {
        var claimRequest = new ClaimDepositRequest(
            txid: deposit.txid,
            vout: deposit.vout,
            maxFee: new MaxFee(
                onchain: new MaxOnchainFee.Fixed(amount: requiredFee),
                proportionalPpm: null
            )
        );
        await sdk.ClaimDeposit(request: claimRequest);
    }
}
Javascript
if (deposit.claimError?.type === 'maxDepositClaimFeeExceeded') {
  const requiredFee = deposit.claimError.requiredFeeSats

  // Accept up to 1% of the deposit amount, but never more than 5,000 sats
  const maxFeeSats = Math.min(deposit.amountSats / 100, 5000)
  if (requiredFee <= maxFeeSats) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: { onchain: { type: 'fixed', amount: requiredFee } }
    }
    await sdk.claimDeposit(claimRequest)
  }
}
React Native
if (deposit.claimError?.tag === DepositClaimError_Tags.MaxDepositClaimFeeExceeded) {
  const requiredFee = deposit.claimError.inner.requiredFeeSats

  // Accept up to 1% of the deposit amount, but never more than 5,000 sats
  const onePercent = deposit.amountSats / BigInt(100)
  const maxFeeSats = onePercent < BigInt(5000) ? onePercent : BigInt(5000)
  if (requiredFee <= maxFeeSats) {
    const claimRequest: ClaimDepositRequest = {
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee.create({
        onchain: new MaxOnchainFee.Fixed({ amount: requiredFee }),
        proportionalPpm: undefined
      })
    }
    await sdk.claimDeposit(claimRequest)
  }
}
Flutter
final claimError = deposit.claimError;
if (claimError is DepositClaimError_MaxDepositClaimFeeExceeded) {
  final requiredFee = claimError.requiredFeeSats;

  // Accept up to 1% of the deposit amount, but never more than 5,000 sats
  final onePercent = deposit.amountSats ~/ BigInt.from(100);
  final cap = BigInt.from(5000);
  final maxFeeSats = onePercent < cap ? onePercent : cap;
  if (requiredFee <= maxFeeSats) {
    final claimRequest = ClaimDepositRequest(
      txid: deposit.txid,
      vout: deposit.vout,
      maxFee: MaxFee(
        onchain: MaxOnchainFee.fixed(amount: requiredFee),
        proportionalPpm: null,
      ),
    );
    await sdk.claimDeposit(request: claimRequest);
  }
}
Python
try:
    if isinstance(
        deposit.claim_error, DepositClaimError.MAX_DEPOSIT_CLAIM_FEE_EXCEEDED
    ):
        required_fee = deposit.claim_error.required_fee_sats

        # Accept up to 1% of the deposit amount, but never more than 5,000 sats
        max_fee_sats = min(deposit.amount_sats // 100, 5_000)
        if required_fee <= max_fee_sats:
            claim_request = ClaimDepositRequest(
                txid=deposit.txid,
                vout=deposit.vout,
                max_fee=MaxFee(
                    onchain=MaxOnchainFee.FIXED(amount=required_fee),
                    proportional_ppm=None,
                ),
            )
            await sdk.claim_deposit(request=claim_request)
except Exception as error:
    logging.error(error)
    raise
Go
if claimErr := *deposit.ClaimError; claimErr != nil {
	if exceeded, ok := claimErr.(breez_sdk_spark.DepositClaimErrorMaxDepositClaimFeeExceeded); ok {
		requiredFee := exceeded.RequiredFeeSats

		// Accept up to 1% of the deposit amount, but never more than 5,000 sats
		maxFeeSats := deposit.AmountSats / 100
		if maxFeeSats > 5_000 {
			maxFeeSats = 5_000
		}
		if requiredFee <= maxFeeSats {
			maxFee := breez_sdk_spark.MaxFee{
				Onchain: breez_sdk_spark.MaxOnchainFeeFixed{Amount: requiredFee},
			}
			claimRequest := breez_sdk_spark.ClaimDepositRequest{
				Txid:   deposit.Txid,
				Vout:   deposit.Vout,
				MaxFee: &maxFee,
			}
			_, err := sdk.ClaimDeposit(claimRequest)
			if err != nil {
				var sdkErr *breez_sdk_spark.SdkError
				if errors.As(err, &sdkErr) {
					// Handle SdkError - can inspect specific variants if needed
					// e.g., switch on sdkErr variant for InsufficientFunds, NetworkError, etc.
				}
				return err
			}
		}
	}
}