Withdraw Bitcoin from Spark to any L1 address via cooperative exit.
Withdrawal Flow
The complete process for withdrawing Bitcoin from Spark back to Layer 1:
Get Fee Quote
Request a fee quote for your withdrawal with different speed options. const feeQuote = await wallet . getWithdrawalFeeQuote ({
amountSats: 50000 ,
withdrawalAddress: "bc1p..." // Your Bitcoin address
});
console . log ( "Fee quote:" , feeQuote );
Initiate Withdrawal
Start the withdrawal process using the fee quote. // Calculate fee based on exit speed
const exitSpeed = "fast" ; // or "medium", "slow"
let feeAmountSats : number ;
switch ( exitSpeed ) {
case "fast" :
feeAmountSats = ( feeQuote . l1BroadcastFeeFast ?. originalValue || 0 ) +
( feeQuote . userFeeFast ?. originalValue || 0 );
break ;
case "medium" :
feeAmountSats = ( feeQuote . l1BroadcastFeeMedium ?. originalValue || 0 ) +
( feeQuote . userFeeMedium ?. originalValue || 0 );
break ;
case "slow" :
feeAmountSats = ( feeQuote . l1BroadcastFeeSlow ?. originalValue || 0 ) +
( feeQuote . userFeeSlow ?. originalValue || 0 );
break ;
}
const withdrawal = await wallet . withdraw ({
onchainAddress: "bc1p..." , // Your Bitcoin address
exitSpeed: exitSpeed ,
amountSats: 50000 ,
feeQuoteId: feeQuote . id ,
feeAmountSats: feeAmountSats ,
deductFeeFromWithdrawalAmount: false
});
console . log ( "Withdrawal initiated:" , withdrawal );
Monitor Status
Track the withdrawal status until completion. const exitRequest = await wallet . getCoopExitRequest ( withdrawal . id );
console . log ( "Withdrawal status:" , exitRequest . status );
console . log ( "Transaction ID:" , exitRequest . coopExitTxid );
Get Withdrawal Fee Quote
Request fee quotes for different withdrawal speeds before initiating a withdrawal.
getWithdrawalFeeQuote(params)
Gets a fee quote for a cooperative exit (on-chain withdrawal). The quote includes options for different speeds and an expiry time and must be passed to withdraw before it expires.
const feeQuote = await wallet . getWithdrawalFeeQuote ({
amountSats: 100000 ,
withdrawalAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297"
});
console . log ( "Fee quote:" , feeQuote );
console . log ( "Fast option:" , feeQuote . fast );
console . log ( "Medium option:" , feeQuote . medium );
console . log ( "Slow option:" , feeQuote . slow );
The amount in satoshis to withdraw
The Bitcoin address where the funds should be sent
feeQuote
CoopExitFeeQuote | null
required
A fee quote for the withdrawal, or null if not available
Initiate Withdrawal
Start the withdrawal process using a fee quote.
withdraw(params)
Initiates a cooperative exit to withdraw Bitcoin from Spark to Layer 1.
// Calculate fee based on exit speed
const exitSpeed = "fast" ; // "fast", "medium", or "slow"
let feeAmountSats : number ;
switch ( exitSpeed ) {
case "fast" :
feeAmountSats = ( feeQuote . l1BroadcastFeeFast ?. originalValue || 0 ) +
( feeQuote . userFeeFast ?. originalValue || 0 );
break ;
case "medium" :
feeAmountSats = ( feeQuote . l1BroadcastFeeMedium ?. originalValue || 0 ) +
( feeQuote . userFeeMedium ?. originalValue || 0 );
break ;
case "slow" :
feeAmountSats = ( feeQuote . l1BroadcastFeeSlow ?. originalValue || 0 ) +
( feeQuote . userFeeSlow ?. originalValue || 0 );
break ;
}
const withdrawal = await wallet . withdraw ({
onchainAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297" ,
exitSpeed: exitSpeed ,
amountSats: 100000 ,
feeQuoteId: feeQuote . id ,
feeAmountSats: feeAmountSats ,
deductFeeFromWithdrawalAmount: false
});
console . log ( "Withdrawal ID:" , withdrawal . id );
console . log ( "Status:" , withdrawal . status );
The Bitcoin address to send the funds to
The withdrawal speed: “fast”, “medium”, or “slow”
The amount in satoshis to withdraw (if not specified, withdraws all available funds)
The ID from the fee quote returned by getWithdrawalFeeQuote
The fee amount in satoshis based on the chosen exitSpeed
Deprecated: Use feeQuoteId and feeAmountSats instead
deductFeeFromWithdrawalAmount
Whether to deduct the fee from the withdrawal amount (default: true)
The withdrawal request details including ID and status
Monitor Withdrawal Status
Track the status of your withdrawal request.
getCoopExitRequest(id)
Gets a cooperative exit request by ID to check withdrawal status.
const exitRequest = await wallet . getCoopExitRequest ( withdrawalId );
console . log ( "Withdrawal status:" , exitRequest . status );
console . log ( "Transaction ID:" , exitRequest . coopExitTxid );
console . log ( "Fee:" , exitRequest . fee . originalValue , exitRequest . fee . originalUnit );
// Check if withdrawal is complete
if ( exitRequest . status === "SUCCEEDED" ) {
console . log ( "Withdrawal completed successfully!" );
}
The ID of the cooperative exit request
exitRequest
CoopExitRequest | null
required
The cooperative exit request details or null if not found
Withdrawal Speeds
Spark offers three withdrawal speed options with different fee structures:
Fast Highest priority
Higher fees
~1-2 hours
Best for urgent withdrawals
Medium Balanced option
Moderate fees
~4-6 hours
Good for most use cases
Slow Lowest priority
Lowest fees
~12-24 hours
Best for non-urgent withdrawals
Real-time Withdrawal Monitoring
Monitor withdrawal status by polling getCoopExitRequest().
Withdrawals don’t emit events. Poll the status using getCoopExitRequest() with the withdrawal ID returned from withdraw().
// Check withdrawal status periodically
const checkWithdrawalStatus = async ( withdrawalId ) => {
const exitRequest = await wallet . getCoopExitRequest ( withdrawalId );
switch ( exitRequest . status ) {
case "SUCCEEDED" :
console . log ( "Withdrawal completed!" );
console . log ( "On-chain txid:" , exitRequest . coopExitTxid );
return true ;
case "FAILED" :
console . log ( "Withdrawal failed" );
return false ;
default :
console . log ( "Withdrawal pending..." );
setTimeout (() => checkWithdrawalStatus ( withdrawalId ), 30000 ); // Check again in 30 seconds
return false ;
}
};
Error Handling
Implement proper error handling for withdrawal operations.
async function withdrawSafely ( params ) {
try {
// 1. Get fee quote first
const feeQuote = await wallet . getWithdrawalFeeQuote ({
amountSats: params . amountSats ,
withdrawalAddress: params . onchainAddress
});
if ( ! feeQuote ) {
throw new Error ( "Unable to get fee quote" );
}
// 2. Check if quote is still valid
const now = Date . now ();
if ( feeQuote . expiryTime && now > feeQuote . expiryTime ) {
throw new Error ( "Fee quote expired, please get a new one" );
}
// 3. Calculate fee based on exit speed
let feeAmountSats : number ;
switch ( params . exitSpeed ) {
case "fast" :
feeAmountSats = ( feeQuote . l1BroadcastFeeFast ?. originalValue || 0 ) +
( feeQuote . userFeeFast ?. originalValue || 0 );
break ;
case "medium" :
feeAmountSats = ( feeQuote . l1BroadcastFeeMedium ?. originalValue || 0 ) +
( feeQuote . userFeeMedium ?. originalValue || 0 );
break ;
case "slow" :
feeAmountSats = ( feeQuote . l1BroadcastFeeSlow ?. originalValue || 0 ) +
( feeQuote . userFeeSlow ?. originalValue || 0 );
break ;
}
// 4. Initiate withdrawal
const withdrawal = await wallet . withdraw ({
... params ,
feeQuoteId: feeQuote . id ,
feeAmountSats: feeAmountSats
});
console . log ( "Withdrawal initiated successfully:" , withdrawal . id );
return withdrawal ;
} catch ( error ) {
console . error ( "Withdrawal failed:" , error . message );
// Handle specific error types
if ( error . message . includes ( "Insufficient" )) {
console . log ( "Please check your balance" );
} else if ( error . message . includes ( "expired" )) {
console . log ( "Please get a new fee quote" );
} else if ( error . message . includes ( "Invalid address" )) {
console . log ( "Please verify the withdrawal address" );
}
throw error ;
}
}
Example: Complete Withdrawal Flow
import { SparkWallet } from "@buildonspark/spark-sdk" ;
async function completeWithdrawal () {
const { wallet } = await SparkWallet . initialize ({
options: { network: "REGTEST" }
});
try {
// 1. Check current balance
const balance = await wallet . getBalance ();
console . log ( "Current balance:" , balance . balance , "sats" );
// 2. Set up event listeners
wallet . on ( "transfer:claimed" , ( transferId , updatedBalance ) => {
console . log ( `Transfer ${ transferId } claimed. New balance: ${ updatedBalance } sats` );
});
// 3. Get fee quote
const feeQuote = await wallet . getWithdrawalFeeQuote ({
amountSats: 50000 ,
withdrawalAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297"
});
console . log ( "Fee quote:" , feeQuote );
// 4. Calculate fee based on exit speed
const exitSpeed = "medium" ;
let feeAmountSats : number ;
switch ( exitSpeed ) {
case "fast" :
feeAmountSats = ( feeQuote . l1BroadcastFeeFast ?. originalValue || 0 ) +
( feeQuote . userFeeFast ?. originalValue || 0 );
break ;
case "medium" :
feeAmountSats = ( feeQuote . l1BroadcastFeeMedium ?. originalValue || 0 ) +
( feeQuote . userFeeMedium ?. originalValue || 0 );
break ;
case "slow" :
feeAmountSats = ( feeQuote . l1BroadcastFeeSlow ?. originalValue || 0 ) +
( feeQuote . userFeeSlow ?. originalValue || 0 );
break ;
}
// 5. Initiate withdrawal
const withdrawal = await wallet . withdraw ({
onchainAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297" ,
exitSpeed: exitSpeed ,
amountSats: 50000 ,
feeQuoteId: feeQuote . id ,
feeAmountSats: feeAmountSats ,
deductFeeFromWithdrawalAmount: false
});
console . log ( "Withdrawal initiated:" , withdrawal . id );
// 6. Monitor withdrawal status
const monitorWithdrawal = async () => {
const exitRequest = await wallet . getCoopExitRequest ( withdrawal . id );
if ( exitRequest . status === "SUCCEEDED" ) {
console . log ( "Withdrawal completed successfully!" );
console . log ( "Transaction ID:" , exitRequest . coopExitTxid );
} else if ( exitRequest . status === "FAILED" ) {
console . log ( "Withdrawal failed" );
} else {
console . log ( "Withdrawal pending..." );
setTimeout ( monitorWithdrawal , 30000 ); // Check again in 30 seconds
}
};
await monitorWithdrawal ();
} catch ( error ) {
console . error ( "Withdrawal failed:" , error );
}
}