Skip to main content
Learn how to withdraw Bitcoin from your Spark wallet back to Layer 1. This guide covers cooperative exits, fee estimation, and the complete withdrawal process.
Withdraw to L1

Withdrawal Flow

The complete process for withdrawing Bitcoin from Spark back to Layer 1:
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);
2

Initiate Withdrawal

Start the withdrawal process using the fee quote.
const withdrawal = await wallet.withdraw({
  onchainAddress: "bc1p...", // Your Bitcoin address
  exitSpeed: "fast", // or "medium", "slow"
  amountSats: 50000,
  feeQuote: feeQuote,
  deductFeeFromWithdrawalAmount: false
});
console.log("Withdrawal initiated:", withdrawal);
3

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.txid);

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);

Initiate Withdrawal

Start the withdrawal process using a fee quote. withdraw(params) Initiates a cooperative exit to withdraw Bitcoin from Spark to Layer 1.
const withdrawal = await wallet.withdraw({
  onchainAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297",
  exitSpeed: "fast", // "fast", "medium", or "slow"
  amountSats: 100000,
  feeQuote: feeQuote,
  deductFeeFromWithdrawalAmount: false
});

console.log("Withdrawal ID:", withdrawal.id);
console.log("Status:", withdrawal.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.txid);
console.log("Amount:", exitRequest.amountSats, "sats");

// Check if withdrawal is complete
if (exitRequest.status === "completed") {
  console.log("Withdrawal completed successfully!");
}

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 using event listeners.
// Listen for withdrawal events
wallet.on("transfer:claimed", (transferId, updatedBalance) => {
  console.log(`Transfer ${transferId} claimed. New balance: ${updatedBalance} sats`);
});

// Check withdrawal status periodically
const checkWithdrawalStatus = async (withdrawalId) => {
  const exitRequest = await wallet.getCoopExitRequest(withdrawalId);
  
  switch (exitRequest.status) {
    case "completed":
      console.log("Withdrawal completed!");
      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. Initiate withdrawal
    const withdrawal = await wallet.withdraw({
      ...params,
      feeQuote: feeQuote
    });

    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;
  }
}

Best Practices

  • Get fresh quotes - Fee quotes expire, always get a new one before withdrawing
  • Choose appropriate speed - Balance urgency with cost for your use case
  • Monitor status - Use polling or events to track withdrawal progress
  • Handle errors - Implement proper error handling for failed withdrawals
  • Verify addresses - Always validate Bitcoin addresses before withdrawing
  • Test first - Use regtest network for testing withdrawal flows

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. Initiate withdrawal
    const withdrawal = await wallet.withdraw({
      onchainAddress: "bc1p5d7rjq7g6rdk2yhzks9smtbqtedr4dekq08ge8ztwac72sfr9rusxg3297",
      exitSpeed: "medium",
      amountSats: 50000,
      feeQuote: feeQuote,
      deductFeeFromWithdrawalAmount: false
    });

    console.log("Withdrawal initiated:", withdrawal.id);

    // 5. Monitor withdrawal status
    const monitorWithdrawal = async () => {
      const exitRequest = await wallet.getCoopExitRequest(withdrawal.id);
      
      if (exitRequest.status === "completed") {
        console.log("Withdrawal completed successfully!");
        console.log("Transaction ID:", exitRequest.txid);
      } 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);
  }
}