Stable balance
The stable balance feature enables users to convert between Bitcoin and a stable token, like USDB, protecting against Bitcoin price volatility. On receive, sats are converted to the stable token. On send, the stable token is converted back to Bitcoin.
How it works
When stable balance is configured and activated, for example with USDB, the SDK manages conversions in both directions using token conversions:
- On receive — When you receive a payment (Lightning, Spark, or on-chain), the SDK converts the incoming sats to USDB once your sats balance exceeds the configured threshold.
- On send — When you send a Bitcoin payment and your sats balance is insufficient, the SDK converts USDB back to Bitcoin to cover the payment. See Sending payments with stable balance for more details.
Your balance remains stable in value, denominated in USDB.
Configuration
To enable stable balance, configure the stable balance config when initializing the SDK:
- Tokens — The stable token to use. Specify its token identifier and a display label.
- Default Active Label — Optional label to activate by default. If unset, stable balance starts deactivated and can be activated at runtime via user settings.
- Threshold Sats — Optional minimum sats balance to trigger conversion. We recommend omitting this to use the conversion limit minimum.
- Maximum Slippage — Optional maximum slippage in basis points. We recommend omitting this to use the default of 10 bps (0.1%).
let mut config = default_config(Network::Mainnet);
// Enable stable balance with USDB conversion
config.stable_balance_config = Some(StableBalanceConfig {
tokens: vec![StableBalanceToken {
label: "USDB".to_string(),
token_identifier: "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87".to_string(),
}],
default_active_label: Some("USDB".to_string()),
threshold_sats: None,
max_slippage_bps: None,
});
var config = defaultConfig(network: Network.mainnet)
// Enable stable balance with USDB conversion
config.stableBalanceConfig = StableBalanceConfig(
tokens: [StableBalanceToken(
label: "USDB",
tokenIdentifier: "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87"
)],
defaultActiveLabel: "USDB"
)
val config = defaultConfig(Network.MAINNET)
// Enable stable balance with USDB conversion
config.stableBalanceConfig = StableBalanceConfig(
tokens = listOf(StableBalanceToken(
label = "USDB",
tokenIdentifier = "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87",
)),
defaultActiveLabel = "USDB",
)
var config = BreezSdkSparkMethods.DefaultConfig(Network.Mainnet) with
{
// Enable stable balance with USDB conversion
stableBalanceConfig = new StableBalanceConfig(
tokens: new StableBalanceToken[] {
new StableBalanceToken(
label: "USDB",
tokenIdentifier: "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87"
)
},
defaultActiveLabel: "USDB"
)
};
const config = defaultConfig('mainnet')
// Enable stable balance with USDB conversion
config.stableBalanceConfig = {
tokens: [{
label: 'USDB',
tokenIdentifier: 'btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87'
}],
defaultActiveLabel: 'USDB'
}
const config = defaultConfig(Network.Mainnet)
// Enable stable balance with USDB conversion
config.stableBalanceConfig = {
tokens: [{
label: 'USDB',
tokenIdentifier: 'btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87'
}],
defaultActiveLabel: 'USDB',
thresholdSats: undefined,
maxSlippageBps: undefined
}
var config = defaultConfig(network: Network.mainnet).copyWith(
// Enable stable balance with USDB conversion
stableBalanceConfig: StableBalanceConfig(
tokens: [StableBalanceToken(
label: "USDB",
tokenIdentifier: "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87",
)],
defaultActiveLabel: "USDB",
));
config = default_config(network=Network.MAINNET)
# Enable stable balance with USDB conversion
config.stable_balance_config = StableBalanceConfig(
tokens=[StableBalanceToken(
label="USDB",
token_identifier="btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87",
)],
default_active_label="USDB",
)
config := breez_sdk_spark.DefaultConfig(breez_sdk_spark.NetworkMainnet)
// Enable stable balance with USDB conversion
defaultActiveLabel := "USDB"
stableBalanceConfig := breez_sdk_spark.StableBalanceConfig{
Tokens: []breez_sdk_spark.StableBalanceToken{
{Label: "USDB", TokenIdentifier: "btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87"},
},
DefaultActiveLabel: &defaultActiveLabel,
}
config.StableBalanceConfig = &stableBalanceConfig
Developer note
If the configured threshold sats is lower than the minimum amount required by the conversion protocol, the protocol minimum will be used instead. This ensures conversions always meet the minimum requirements.
Switching stable balance mode
You can activate, switch, or deactivate stable balance at runtime using the user settings API. This allows users to choose when to enable stable balance and which token to use.
Activating stable balance
To activate stable balance, set the active label to one of the labels defined in your #{{name StableBalanceConfig.tokens}} list:
sdk.update_user_settings(UpdateUserSettingsRequest {
spark_private_mode_enabled: None,
stable_balance_active_label: Some(StableBalanceActiveLabel::Set {
label: "USDB".to_string(),
}),
})
.await?;
try await sdk.updateUserSettings(
request: UpdateUserSettingsRequest(
sparkPrivateModeEnabled: nil,
stableBalanceActiveLabel: .set(label: "USDB")
))
try {
sdk.updateUserSettings(UpdateUserSettingsRequest(
sparkPrivateModeEnabled = null,
stableBalanceActiveLabel = StableBalanceActiveLabel.Set(label = "USDB")
))
} catch (e: Exception) {
// handle error
}
await sdk.UpdateUserSettings(
request: new UpdateUserSettingsRequest(
sparkPrivateModeEnabled: null,
stableBalanceActiveLabel: new StableBalanceActiveLabel.Set(label: "USDB")
)
);
await sdk.updateUserSettings({
stableBalanceActiveLabel: { type: 'set', label: 'USDB' }
})
await sdk.updateUserSettings({
sparkPrivateModeEnabled: undefined,
stableBalanceActiveLabel: new StableBalanceActiveLabel.Set({ label: 'USDB' })
})
await sdk.updateUserSettings(
request: UpdateUserSettingsRequest(
stableBalanceActiveLabel: StableBalanceActiveLabel_Set(label: "USDB")));
try:
await sdk.update_user_settings(
request=UpdateUserSettingsRequest(
spark_private_mode_enabled=None,
stable_balance_active_label=StableBalanceActiveLabel.SET(label="USDB")
)
)
except Exception as error:
logging.error(error)
raise
activeLabel := breez_sdk_spark.StableBalanceActiveLabel(
breez_sdk_spark.StableBalanceActiveLabelSet{Label: "USDB"},
)
err := sdk.UpdateUserSettings(breez_sdk_spark.UpdateUserSettingsRequest{
StableBalanceActiveLabel: &activeLabel,
})
if err != nil {
return err
}
When activated, the SDK immediately converts any excess sats balance to the specified token.
Deactivating stable balance
To deactivate stable balance, unset the active label:
sdk.update_user_settings(UpdateUserSettingsRequest {
spark_private_mode_enabled: None,
stable_balance_active_label: Some(StableBalanceActiveLabel::Unset),
})
.await?;
try await sdk.updateUserSettings(
request: UpdateUserSettingsRequest(
sparkPrivateModeEnabled: nil,
stableBalanceActiveLabel: .unset
))
try {
sdk.updateUserSettings(UpdateUserSettingsRequest(
sparkPrivateModeEnabled = null,
stableBalanceActiveLabel = StableBalanceActiveLabel.Unset
))
} catch (e: Exception) {
// handle error
}
await sdk.UpdateUserSettings(
request: new UpdateUserSettingsRequest(
sparkPrivateModeEnabled: null,
stableBalanceActiveLabel: new StableBalanceActiveLabel.Unset()
)
);
await sdk.updateUserSettings({
stableBalanceActiveLabel: { type: 'unset' }
})
await sdk.updateUserSettings({
sparkPrivateModeEnabled: undefined,
stableBalanceActiveLabel: new StableBalanceActiveLabel.Unset()
})
await sdk.updateUserSettings(
request: UpdateUserSettingsRequest(
stableBalanceActiveLabel: StableBalanceActiveLabel_Unset()));
try:
await sdk.update_user_settings(
request=UpdateUserSettingsRequest(
spark_private_mode_enabled=None,
stable_balance_active_label=StableBalanceActiveLabel.UNSET()
)
)
except Exception as error:
logging.error(error)
raise
activeLabel := breez_sdk_spark.StableBalanceActiveLabel(
breez_sdk_spark.StableBalanceActiveLabelUnset{},
)
err := sdk.UpdateUserSettings(breez_sdk_spark.UpdateUserSettingsRequest{
StableBalanceActiveLabel: &activeLabel,
})
if err != nil {
return err
}
When deactivated, the SDK converts any remaining token balance back to Bitcoin.
Checking the current mode
You can check which token is currently active using get_user_settingsget_user_settingsgetUserSettingsgetUserSettingsgetUserSettingsgetUserSettingsgetUserSettingsGetUserSettingsGetUserSettings:
let user_settings = sdk.get_user_settings().await?;
info!("User settings: {:?}", user_settings);
let userSettings = try await sdk.getUserSettings()
print("User settings: \(userSettings)")
try {
val userSettings = sdk.getUserSettings()
println("User settings: $userSettings")
} catch (e: Exception) {
// handle error
}
var userSettings = await sdk.GetUserSettings();
Console.WriteLine($"User settings: {userSettings}");
const userSettings = await sdk.getUserSettings()
console.log(`User settings: ${JSON.stringify(userSettings)}`)
const userSettings = await sdk.getUserSettings()
console.log(`User settings: ${JSON.stringify(userSettings)}`)
final userSettings = await sdk.getUserSettings();
print('User settings: $userSettings');
try:
user_settings = await sdk.get_user_settings()
print(f"User settings: {user_settings}")
except Exception as error:
logging.error(error)
raise
userSettings, err := sdk.GetUserSettings()
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
}
log.Printf("User settings: %v", userSettings)
The stable_balance_active_labelstable_balance_active_labelstableBalanceActiveLabelstableBalanceActiveLabelstableBalanceActiveLabelstableBalanceActiveLabelstableBalanceActiveLabelStableBalanceActiveLabelStableBalanceActiveLabel field will be unset if stable balance is deactivated, or the label of the currently active token.
Sending payments with stable balance
When your balance is held in a stable token, you can still send Bitcoin payments. The SDK detects when there's not enough Bitcoin balance to cover a payment and sets up the token-to-Bitcoin conversion for you.
When you prepare to send a payment without specifying conversion options:
- If you have enough Bitcoin balance, no conversion is needed
- If your Bitcoin balance is insufficient, the SDK configures conversion options using your stable balance settings (token identifier and slippage)
Developer note
You can still explicitly specify conversion options in your request if you need custom slippage settings or want to override the default behavior.
Sending entire balance
When stable balance is active, you can send your entire wallet balance — both the token balance and any remaining Bitcoin — in a single payment. This is useful for draining a wallet completely.
To send all, provide the full token balance as the amount along with FeePolicy::FeesIncludedFeePolicy.FEES_INCLUDEDFeePolicy.feesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicy.FeesIncludedFeePolicyFeesIncludedFeePolicy.FeesIncluded and ConversionType::ToBitcoinConversionType.TO_BITCOINConversionType.toBitcoinConversionType.ToBitcoinConversionType.ToBitcoinConversionType.ToBitcoinConversionType.ToBitcoinConversionTypeToBitcoinConversionType.ToBitcoin conversion options. The SDK converts all specified tokens to Bitcoin, combines the result with any existing Bitcoin balance, and deducts payment fees from the total.
The prepare response returns the estimated total Bitcoin available after conversion, and includes a conversion_estimateconversion_estimateconversionEstimateconversionEstimateconversionEstimateconversionEstimateconversionEstimateConversionEstimateConversionEstimate with the conversion details.
The same approach works with prepare_lnurl_payprepare_lnurl_payprepareLnurlPayprepareLnurlPayprepareLnurlPayprepareLnurlPayprepareLnurlPayPrepareLnurlPayPrepareLnurlPay for LNURL payments.
let payment_request = "<payment request>".to_string();
let token_identifier = "<token identifier>".to_string();
let info = sdk
.get_info(GetInfoRequest {
ensure_synced: Some(false),
})
.await?;
let token_balance = info
.token_balances
.get(&token_identifier)
.ok_or_else(|| anyhow::anyhow!("Token balance not found"))?;
let conversion_options = Some(ConversionOptions {
conversion_type: ConversionType::ToBitcoin {
from_token_identifier: token_identifier.clone(),
},
max_slippage_bps: None,
completion_timeout_secs: None,
});
let prepare_response = sdk
.prepare_send_payment(PrepareSendPaymentRequest {
payment_request,
amount: Some(token_balance.balance),
token_identifier: Some(token_identifier),
conversion_options,
fee_policy: Some(FeePolicy::FeesIncluded),
})
.await?;
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
info!("Total sats available: {}", prepare_response.amount);
if let Some(conversion_estimate) = &prepare_response.conversion_estimate {
info!("Converting {} token units → ~{} sats", conversion_estimate.amount_in, conversion_estimate.amount_out);
info!("Conversion fee: {} token units", conversion_estimate.fee);
}
let paymentRequest = "<payment request>"
let tokenIdentifier = "<token identifier>"
let info = try await sdk.getInfo(
request: GetInfoRequest(ensureSynced: false))
guard let tokenBalance = info.tokenBalances[tokenIdentifier] else {
throw SdkError.InvalidInput("Token balance not found")
}
let conversionOptions = ConversionOptions(
conversionType: ConversionType.toBitcoin(
fromTokenIdentifier: tokenIdentifier
),
maxSlippageBps: nil,
completionTimeoutSecs: nil
)
let prepareResponse = try await sdk.prepareSendPayment(
request: PrepareSendPaymentRequest(
paymentRequest: paymentRequest,
amount: tokenBalance.balance,
tokenIdentifier: tokenIdentifier,
conversionOptions: conversionOptions,
feePolicy: .feesIncluded
))
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
print("Total sats available: \(prepareResponse.amount)")
if let conversionEstimate = prepareResponse.conversionEstimate {
print("Converting \(conversionEstimate.amountIn) token units → ~\(conversionEstimate.amountOut) sats")
print("Conversion fee: \(conversionEstimate.fee) token units")
}
val paymentRequest = "<payment request>"
val tokenIdentifier = "<token identifier>"
try {
val info = sdk.getInfo(GetInfoRequest(false))
val tokenBalance = info.tokenBalances[tokenIdentifier]
?: throw Exception("Token balance not found")
val conversionOptions = ConversionOptions(
conversionType = ConversionType.ToBitcoin(
tokenIdentifier
),
maxSlippageBps = null,
completionTimeoutSecs = null
)
val req = PrepareSendPaymentRequest(
paymentRequest,
amount = tokenBalance.balance,
tokenIdentifier = tokenIdentifier,
conversionOptions = conversionOptions,
feePolicy = FeePolicy.FEES_INCLUDED,
)
val prepareResponse = sdk.prepareSendPayment(req)
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
// Log.v("Breez", "Total sats available: ${prepareResponse.amount}")
prepareResponse.conversionEstimate?.let { conversionEstimate ->
// Log.v("Breez", "Converting ${conversionEstimate.amountIn} token units → ~${conversionEstimate.amountOut} sats")
// Log.v("Breez", "Conversion fee: ${conversionEstimate.fee} token units")
}
} catch (e: Exception) {
// handle error
}
var paymentRequest = "<payment request>";
var tokenIdentifier = "<token identifier>";
var info = await sdk.GetInfo(request: new GetInfoRequest(ensureSynced: false));
if (!info.tokenBalances.TryGetValue(tokenIdentifier, out var tokenBalance))
{
throw new Exception("Token balance not found");
}
var conversionOptions = new ConversionOptions(
conversionType: new ConversionType.ToBitcoin(
fromTokenIdentifier: tokenIdentifier
),
maxSlippageBps: null,
completionTimeoutSecs: null
);
var request = new PrepareSendPaymentRequest(
paymentRequest: paymentRequest,
amount: tokenBalance.balance,
tokenIdentifier: tokenIdentifier,
conversionOptions: conversionOptions,
feePolicy: FeePolicy.FeesIncluded
);
var prepareResponse = await sdk.PrepareSendPayment(request: request);
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
Console.WriteLine($"Total sats available: {prepareResponse.amount}");
if (prepareResponse.conversionEstimate != null)
{
Console.WriteLine("Converting " +
$"{prepareResponse.conversionEstimate.amountIn} token units → ~{prepareResponse.conversionEstimate.amountOut} sats");
Console.WriteLine("Conversion fee: " +
$"{prepareResponse.conversionEstimate.fee} token units");
}
const paymentRequest = '<payment request>'
const tokenIdentifier = '<token identifier>'
const info = await sdk.getInfo({ ensureSynced: false })
const tokenBalance = info.tokenBalances.get(tokenIdentifier)
if (tokenBalance === undefined) {
throw new Error('Token balance not found')
}
const conversionOptions: ConversionOptions = {
conversionType: {
type: 'toBitcoin',
fromTokenIdentifier: tokenIdentifier
}
}
const feePolicy: FeePolicy = 'feesIncluded'
const prepareResponse = await sdk.prepareSendPayment({
paymentRequest,
amount: tokenBalance.balance,
tokenIdentifier,
conversionOptions,
feePolicy
})
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
console.log(`Total sats available: ${prepareResponse.amount}`)
if (prepareResponse.conversionEstimate !== undefined) {
const estimate = prepareResponse.conversionEstimate
console.log(`Converting ${estimate.amountIn} token units → ~${estimate.amountOut} sats`)
console.log(`Conversion fee: ${estimate.fee} token units`)
}
const paymentRequest = '<payment request>'
const tokenIdentifier = '<token identifier>'
const info = await sdk.getInfo({ ensureSynced: false })
const tokenBalance = info.tokenBalances.get(tokenIdentifier)
if (tokenBalance === undefined) {
throw new Error('Token balance not found')
}
const conversionOptions = {
conversionType: new ConversionType.ToBitcoin({
fromTokenIdentifier: tokenIdentifier
}),
maxSlippageBps: undefined,
completionTimeoutSecs: undefined
}
const prepareResponse = await sdk.prepareSendPayment({
paymentRequest,
amount: tokenBalance.balance,
tokenIdentifier,
conversionOptions,
feePolicy: FeePolicy.FeesIncluded
})
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
console.log(`Total sats available: ${prepareResponse.amount}`)
if (prepareResponse.conversionEstimate !== undefined) {
const estimate = prepareResponse.conversionEstimate
console.log(`Converting ${estimate.amountIn} token units → ~${estimate.amountOut} sats`)
console.log(`Conversion fee: ${estimate.fee} token units`)
}
String paymentRequest = "<payment request>";
String tokenIdentifier = "<token identifier>";
final info = await sdk.getInfo(request: GetInfoRequest(ensureSynced: false));
final tokenBalance = info.tokenBalances[tokenIdentifier];
if (tokenBalance == null) {
throw Exception("Token balance not found");
}
final conversionOptions = ConversionOptions(
conversionType: ConversionType.toBitcoin(
fromTokenIdentifier: tokenIdentifier,
),
);
final request = PrepareSendPaymentRequest(
paymentRequest: paymentRequest,
amount: tokenBalance.balance,
tokenIdentifier: tokenIdentifier,
conversionOptions: conversionOptions,
feePolicy: FeePolicy.feesIncluded);
final response = await sdk.prepareSendPayment(request: request);
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
print("Total sats available: ${response.amount}");
if (response.conversionEstimate != null) {
print(
"Converting ${response.conversionEstimate!.amountIn} token units → ~${response.conversionEstimate!.amountOut} sats");
print(
"Conversion fee: ${response.conversionEstimate!.fee} token units");
}
payment_request = "<payment request>"
token_identifier = "<token identifier>"
try:
info = await sdk.get_info(request=GetInfoRequest(ensure_synced=False))
token_balance = info.token_balances.get(token_identifier)
if token_balance is None:
raise ValueError("Token balance not found")
conversion_options = ConversionOptions(
conversion_type=ConversionType.TO_BITCOIN(
from_token_identifier=token_identifier
),
)
request = PrepareSendPaymentRequest(
payment_request=payment_request,
amount=token_balance.balance,
token_identifier=token_identifier,
conversion_options=conversion_options,
fee_policy=FeePolicy.FEES_INCLUDED,
)
prepare_response = await sdk.prepare_send_payment(request=request)
# The response amount is the estimated total sats available
# (converted sats + existing sat balance)
logging.debug(f"Total sats available: {prepare_response.amount}")
if prepare_response.conversion_estimate is not None:
conversion_estimate = prepare_response.conversion_estimate
logging.debug(
f"Converting {conversion_estimate.amount_in}"
f" token units → ~{conversion_estimate.amount_out} sats"
)
logging.debug(
f"Conversion fee: {conversion_estimate.fee} token units"
)
except Exception as error:
logging.error(error)
raise
paymentRequest := "<payment request>"
tokenIdentifier := "<token identifier>"
ensureSynced := false
info, err := sdk.GetInfo(breez_sdk_spark.GetInfoRequest{
EnsureSynced: &ensureSynced,
})
if err != nil {
return nil, err
}
tokenBalance, ok := info.TokenBalances[tokenIdentifier]
if !ok {
return nil, errors.New("token balance not found")
}
conversionOptions := breez_sdk_spark.ConversionOptions{
ConversionType: breez_sdk_spark.ConversionTypeToBitcoin{
FromTokenIdentifier: tokenIdentifier,
},
}
feePolicy := breez_sdk_spark.FeePolicyFeesIncluded
request := breez_sdk_spark.PrepareSendPaymentRequest{
PaymentRequest: paymentRequest,
Amount: &tokenBalance.Balance,
TokenIdentifier: &tokenIdentifier,
ConversionOptions: &conversionOptions,
FeePolicy: &feePolicy,
}
response, err := sdk.PrepareSendPayment(request)
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 nil, err
}
// The response amount is the estimated total sats available
// (converted sats + existing sat balance)
log.Printf("Total sats available: %v", response.Amount)
if response.ConversionEstimate != nil {
log.Printf("Converting %v token units → ~%v sats", response.ConversionEstimate.AmountIn, response.ConversionEstimate.AmountOut)
log.Printf("Conversion fee: %v token units", response.ConversionEstimate.Fee)
}
Developer note
The actual sats received from conversion may differ slightly from the estimate due to price movement. The SDK handles this by querying the actual balance after conversion completes and sending the full available amount.
Conversion details
Payments involving token conversions include a conversion_detailsconversion_detailsconversionDetailsconversionDetailsconversionDetailsconversionDetailsconversionDetailsConversionDetailsConversionDetails field that describes the conversion that took place. This is useful for displaying conversion context in your UI.
Status
The statusstatusstatusstatusstatusstatusstatusStatusStatus field tracks the lifecycle of the conversion:
| Status | Description |
|---|---|
ConversionStatus::PendingConversionStatus.PENDINGConversionStatus.pendingConversionStatus.PendingConversionStatus.PendingConversionStatus.PendingConversionStatus.PendingConversionStatusPendingConversionStatus.Pending | Conversion is queued or in progress |
ConversionStatus::CompletedConversionStatus.COMPLETEDConversionStatus.completedConversionStatus.CompletedConversionStatus.CompletedConversionStatus.CompletedConversionStatus.CompletedConversionStatusCompletedConversionStatus.Completed | Conversion finished successfully |
ConversionStatus::FailedConversionStatus.FAILEDConversionStatus.failedConversionStatus.FailedConversionStatus.FailedConversionStatus.FailedConversionStatus.FailedConversionStatusFailedConversionStatus.Failed | Conversion could not be completed |
ConversionStatus::RefundNeededConversionStatus.REFUND_NEEDEDConversionStatus.refundNeededConversionStatus.RefundNeededConversionStatus.RefundNeededConversionStatus.RefundNeededConversionStatus.RefundNeededConversionStatusRefundNeededConversionStatus.RefundNeeded | Conversion failed and requires a refund |
ConversionStatus::RefundedConversionStatus.REFUNDEDConversionStatus.refundedConversionStatus.RefundedConversionStatus.RefundedConversionStatus.RefundedConversionStatus.RefundedConversionStatusRefundedConversionStatus.Refunded | Failed conversion has been refunded |
Conversion steps
The fromfromfromfromfromfromfromFromFrom and tototototototoToTo fields are conversion step objects describing each side of the conversion:
| Field | Description |
|---|---|
payment_idpayment_idpaymentIdpaymentIdpaymentIdpaymentIdpaymentIdPaymentIdPaymentId | The ID of the internal conversion payment |
amountamountamountamountamountamountamountAmountAmount | The amount in the step's denomination (sats or token units) |
feefeefeefeefeefeefeeFeeFee | Fee charged for this step |
methodmethodmethodmethodmethodmethodmethodMethodMethod | Payment method (PaymentMethod::SparkPaymentMethod.SPARKPaymentMethod.sparkPaymentMethod.SparkPaymentMethod.SparkPaymentMethod.SparkPaymentMethod.SparkPaymentMethodSparkPaymentMethod.Spark for Bitcoin, PaymentMethod::TokenPaymentMethod.TOKENPaymentMethod.tokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethodTokenPaymentMethod.Token for stable tokens) |
token_metadatatoken_metadatatokenMetadatatokenMetadatatokenMetadatatokenMetadatatokenMetadataTokenMetadataTokenMetadata | Token metadata (name, symbol, etc.) — present when method is PaymentMethod::TokenPaymentMethod.TOKENPaymentMethod.tokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethod.TokenPaymentMethodTokenPaymentMethod.Token |
amount_adjustmentamount_adjustmentamountAdjustmentamountAdjustmentamountAdjustmentamountAdjustmentamountAdjustmentAmountAdjustmentAmountAdjustment | Present if the amount was modified before conversion (see amount adjustments) |
Amount adjustments
The amount_adjustmentamount_adjustmentamountAdjustmentamountAdjustmentamountAdjustmentamountAdjustmentamountAdjustmentAmountAdjustmentAmountAdjustment field is present when the conversion amount was modified before execution:
| Reason | Description |
|---|---|
AmountAdjustmentReason::FlooredToMinLimitAmountAdjustmentReason.FLOORED_TO_MIN_LIMITAmountAdjustmentReason.flooredToMinLimitAmountAdjustmentReason.FlooredToMinLimitAmountAdjustmentReason.FlooredToMinLimitAmountAdjustmentReason.FlooredToMinLimitAmountAdjustmentReason.FlooredToMinLimitAmountAdjustmentReasonFlooredToMinLimitAmountAdjustmentReason.FlooredToMinLimit | Amount was increased to meet the minimum conversion limit |
AmountAdjustmentReason::IncreasedToAvoidDustAmountAdjustmentReason.INCREASED_TO_AVOID_DUSTAmountAdjustmentReason.increasedToAvoidDustAmountAdjustmentReason.IncreasedToAvoidDustAmountAdjustmentReason.IncreasedToAvoidDustAmountAdjustmentReason.IncreasedToAvoidDustAmountAdjustmentReason.IncreasedToAvoidDustAmountAdjustmentReasonIncreasedToAvoidDustAmountAdjustmentReason.IncreasedToAvoidDust | Amount was increased to convert the entire remaining balance, avoiding a leftover too small to convert back |
Related pages
- Token conversion - Learn about converting between Bitcoin and tokens
- Custom configuration - All configuration options
- User settings - Getting and updating user settings
- Handling tokens - Working with tokens in the SDK