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.
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) = "e.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
);
}
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")
}
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
}
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");
}
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`
)
}
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`)
}
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");
}
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
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_requiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredconfirmationsRequiredConfirmationsRequiredConfirmationsRequiredis the depth the claim becomes available at, not the number of blocks left to wait. On theinstantinstantinstantinstantinstantinstantinstantInstantInstantquote, 0 means an instant claim and 1 or 2 an expedited one.- The
instantinstantinstantinstantinstantinstantinstantInstantInstantquote 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_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSatsis the most that claim may cost for this deposit, under the max claim fee that applies to it. Afee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSatswithin it means the SDK makes that claim on its own. It is unset when automatic claiming is off. - The
instantinstantinstantinstantinstantinstantinstantInstantInstantquote shows the provider's price, whether or not it is within itsmax_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats. - The
maturematurematurematurematurematurematureMatureMaturequote is always present. It is flaggedis_estimateis_estimateisEstimateisEstimateisEstimateisEstimateisEstimateIsEstimateIsEstimatewhen 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.
| Quote | What the SDK will do | What to offer |
|---|---|---|
No instantinstantinstantinstantinstantinstantinstantInstantInstant quote | Standard claim | The standard claim only |
instantinstantinstantinstantinstantinstantinstantInstantInstant quote, depth not reached yet | Claim early once the depth arrives, if fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats is within max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats | The 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_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats | Wait for the standard claim | Both, the early claim requiring a higher max fee |
instantinstantinstantinstantinstantinstantinstantInstantInstant quote at a reachable depth, fee_satsfee_satsfeeSatsfeeSatsfeeSatsfeeSatsfeeSatsFeeSatsFeeSats within max_fee_satsmax_fee_satsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsmaxFeeSatsMaxFeeSatsMaxFeeSats | Claim early by itself | The 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:
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?;
}
}
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)
}
}
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
}
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);
}
}
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)
}
}
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)
}
}
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);
}
}
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
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:
| Outcome | Meaning |
|---|---|
ClaimDepositOutcome::SettledClaimDepositOutcome.SETTLEDClaimDepositOutcome.settledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcome.SettledClaimDepositOutcomeSettledClaimDepositOutcome.Settled | A standard claim settled. Carries the resulting payment. |
ClaimDepositOutcome::SubmittedClaimDepositOutcome.SUBMITTEDClaimDepositOutcome.submittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcome.SubmittedClaimDepositOutcomeSubmittedClaimDepositOutcome.Submitted | An 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.Deferred | Nothing 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_quotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuotefetchClaimDepositQuoteFetchClaimDepositQuoteFetchClaimDepositQuotereturns it too, and automatic claims record it inclaim_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_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDepositagain. - 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_depositclaimDepositclaimDepositclaimDepositclaimDepositclaimDepositClaimDepositClaimDepositwithout amax_feemax_feemaxFeemaxFeemaxFeemaxFeemaxFeeMaxFeeMaxFeeclaims 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_feemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeemaxClaimFeeMaxClaimFeeMaxClaimFeeon 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:
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?;
}
}
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)
}
}
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
}
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);
}
}
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)
}
}
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)
}
}
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);
}
}
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
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
}
}
}
}