Bitcoin Integration
This guide covers Bitcoin integration with SwapKit, including wallet connections, UTXO management, cross-chain swaps, and Bitcoin-specific operations.
Overview
Section titled “Overview”Bitcoin is the first and most valuable cryptocurrency, using a UTXO (Unspent Transaction Output) model. SwapKit provides comprehensive Bitcoin support through:
- Bitcoin Toolbox: UTXO management and transaction construction
- Cross-Chain Swaps: Seamless swaps with other cryptocurrencies via THORChain
- Multi-Wallet Support: Compatible with hardware wallets and software wallets
- Fee Optimization: Dynamic fee estimation based on network conditions
- Address Formats: Support for Legacy, SegWit, and Native SegWit addresses
Getting Started
Section titled “Getting Started”Installation
Section titled “Installation”# Full SDK (recommended)bun add @swapkit/sdk
# Individual packages for smaller bundlesbun add @swapkit/toolboxes @swapkit/pluginsBasic Setup
Section titled “Basic Setup”// @noErrorValidationimport { createSwapKit, Chain } from '@swapkit/sdk';
const swapKit = createSwapKit();
const btcWallet = await swapKit.getWallet(Chain.Bitcoin);// @noErrorValidationimport { getBitcoinToolbox } from '@swapkit/toolboxes/utxo';
const btcToolbox = await getBitcoinToolbox({ phrase: "your twelve word mnemonic phrase here",
derivationPath: [84, 0, 0, 0, 0]});
const btcToolbox = await getBitcoinToolbox({ signer: customBitcoinSigner, network: "mainnet"});Bitcoin Toolbox
Section titled “Bitcoin Toolbox”Wallet Connection
Section titled “Wallet Connection”Connect to Bitcoin using various wallet types:
// @noErrorValidationimport { Chain, FeeOption } from "@swapkit/sdk";
await swapKit.connectKeystore([Chain.Bitcoin], "your mnemonic phrase");
await swapKit.connectLedger([Chain.Bitcoin]);
await swapKit.connectTrezor([Chain.Bitcoin]);
await swapKit.connectKeepKey([Chain.Bitcoin]);Address Generation
Section titled “Address Generation”Bitcoin supports multiple address formats:
// @noErrorValidation
const addresses = { nativeSegwit: swapKit.getAddress(Chain.Bitcoin),
legacy: await btcToolbox.getAddress(0, false, "legacy"),
segwit: await btcToolbox.getAddress(0, false, "segwit"),};
console.log("Bitcoin addresses:", addresses);Bitcoin Transfers
Section titled “Bitcoin Transfers”// @noErrorValidationimport { AssetValue } from "@swapkit/sdk";
const txHash = await swapKit.transfer({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.001", }), feeOptionKey: FeeOption.Fast,});
console.log(`Bitcoin transaction hash: ${txHash}`);
const customFeeTx = await btcToolbox.transfer({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.001" }), feeRate: 20,});UTXO Management
Section titled “UTXO Management”Bitcoin uses UTXOs (Unspent Transaction Outputs) instead of account balances:
// @noErrorValidation
const utxos = await btcToolbox.getUTXOs( "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh");
console.log("Available UTXOs:");utxos.forEach((utxo, index) => { console.log( `UTXO ${index}: ${utxo.value} sats (${utxo.txHash}:${utxo.index})` );});
const txParams = await btcToolbox.buildTransaction({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.001" }), feeRate: 15, utxos: utxos.slice(0, 2),});
console.log(`Transaction size: ${txParams.size} bytes`);console.log(`Transaction fee: ${txParams.fee} sats`);Fee Estimation
Section titled “Fee Estimation”Bitcoin fees are dynamic based on network congestion:
// @noErrorValidation
const feeRates = await btcToolbox.getFeeRates();console.log({ slow: feeRates.average, standard: feeRates.fast, fast: feeRates.fastest,});
const feeEstimate = await btcToolbox.estimateTransactionFee({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.01" }), feeOptionKey: FeeOption.Fast,});
console.log(`Estimated fee: ${feeEstimate.toNumber()} sats`);
const feePriorities = [FeeOption.Average, FeeOption.Fast, FeeOption.Fastest];for (const priority of feePriorities) { const fee = await btcToolbox.estimateTransactionFee({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.01" }), feeOptionKey: priority, }); console.log(`${priority} fee: ${fee.toNumber()} sats`);}Transaction History
Section titled “Transaction History”// @noErrorValidation
const transactions = await btcToolbox.getTransactionHistory( "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", 0, 10);
console.log("Recent transactions:");transactions.forEach((tx) => { console.log(`${tx.hash}: ${tx.amount} BTC (${tx.type})`); console.log(` Block: ${tx.blockHeight}, Confirmations: ${tx.confirmations}`); console.log(` Date: ${new Date(tx.date)}`);});
const txDetails = await btcToolbox.getTransaction( "abcd1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd");
console.log("Transaction details:", { hash: txDetails.hash, inputs: txDetails.inputs.length, outputs: txDetails.outputs.length, fee: txDetails.fee, confirmations: txDetails.confirmations,});Cross-Chain Swaps
Section titled “Cross-Chain Swaps”Bitcoin is fully supported in THORChain cross-chain swaps:
// @noErrorValidation
const btcToEthQuote = await swapKit.getQuote({ sellAsset: "BTC.BTC", sellAmount: "0.1", buyAsset: "ETH.ETH", senderAddress: swapKit.getAddress(Chain.Bitcoin), recipientAddress: swapKit.getAddress(Chain.Ethereum),});
console.log("BTC -> ETH quote:", { expectedOutput: btcToEthQuote.expectedOutput, fees: btcToEthQuote.fees, timeEstimate: btcToEthQuote.timeEstimate,});
const swapTx = await swapKit.swap({ route: btcToEthQuote.routes[0], feeOptionKey: FeeOption.Fast,});
const btcToUsdcQuote = await swapKit.getQuote({ sellAsset: "BTC.BTC", sellAmount: "0.05", buyAsset: "ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", senderAddress: swapKit.getAddress(Chain.Bitcoin), recipientAddress: swapKit.getAddress(Chain.Ethereum),});
const btcToRuneQuote = await swapKit.getQuote({ sellAsset: "BTC.BTC", sellAmount: "0.01", buyAsset: "THOR.RUNE", senderAddress: swapKit.getAddress(Chain.Bitcoin), recipientAddress: swapKit.getAddress(Chain.THORChain),});Advanced Bitcoin Features
Section titled “Advanced Bitcoin Features”Multi-Signature Transactions
Section titled “Multi-Signature Transactions”// @noErrorValidation
const publicKeys = [ "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", "03b6d8a48ac51b4c5d0c83b6e0b5e3f4d7e8c9a1b2c3d4e5f6a7b8c9d0e1f2a3b4", "03c7e9f5ac2cd61e8a9b7f4e1d2c3b4a5968a7d6c5b4a398f2e1d7c6b5a49382f1",];
const multiSigAddress = await btcToolbox.createMultiSigAddress(publicKeys, 2);
console.log("Multi-sig address:", multiSigAddress);
const partialTx = await btcToolbox.buildMultiSigTransaction({ multisigAddress: multiSigAddress, recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.005" }), feeRate: 20,});
const signedTx1 = await btcToolbox.signTransaction(partialTx, 0);
const fullySignedTx = await btcToolbox.signTransaction(signedTx1, 1);
const multiSigTxHash = await btcToolbox.broadcastTransaction(fullySignedTx);SegWit Optimization
Section titled “SegWit Optimization”// @noErrorValidation
const nativeSegwitTx = await btcToolbox.transfer({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.01" }), feeRate: 15, addressFormat: "nativeSegwit",});
const legacySize = await btcToolbox.estimateTransactionSize({ inputs: 2, outputs: 2, addressFormat: "legacy",});
const segwitSize = await btcToolbox.estimateTransactionSize({ inputs: 2, outputs: 2, addressFormat: "nativeSegwit",});
console.log(`Legacy tx size: ${legacySize} bytes`);console.log(`SegWit tx size: ${segwitSize} bytes`);console.log( `Size reduction: ${(((legacySize - segwitSize) / legacySize) * 100).toFixed( 1 )}%`);Custom Scripts
Section titled “Custom Scripts”// @noErrorValidation
const scriptHash = "a914b7fcb4e6fb3c3c2dbf55e9e8a28d1e8b5c9a3b2187";
const p2shTx = await btcToolbox.transfer({ recipient: scriptHash, assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.002" }), feeRate: 20, scriptType: "p2sh",});
const timelockTx = await btcToolbox.buildTransaction({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.001" }), feeRate: 15, lockTime: 750000,});Network Configuration
Section titled “Network Configuration”Custom Node Setup
Section titled “Custom Node Setup”// @noErrorValidationimport { SKConfig } from '@swapkit/sdk';
SKConfig.setRpcUrl(Chain.Bitcoin, [ "https: "https: "https:]);
const customBitcoinRpc = "https:SKConfig.setRpcUrl(Chain.Bitcoin, customBitcoinRpc);
const btcToolbox = await getBitcoinToolbox({ phrase: "your mnemonic", network: "mainnet", apiUrl: "https:});Working with Bitcoin Testnet
Section titled “Working with Bitcoin Testnet”// @noErrorValidation
SKConfig.setRpcUrl(Chain.Bitcoin, "https:SKConfig.setEnv('isMainnet', false);
const testnetToolbox = await getBitcoinToolbox({ phrase: "your mnemonic", network: "testnet", derivationPath: [84, 1, 0, 0, 0]});
const testnetAddress = testnetToolbox.getAddress();console.log("Testnet address:", testnetAddress);Error Handling
Section titled “Error Handling”Handle Bitcoin-specific errors:
// @noErrorValidationimport { SwapKitError } from "@swapkit/sdk";
try { await swapKit.transfer({ /* ... */ });} catch (error) { if (error instanceof SwapKitError) { switch (error.code) { case "toolbox_bitcoin_insufficient_funds": console.error("Insufficient Bitcoin balance"); break; case "toolbox_bitcoin_fee_too_high": console.error("Transaction fee exceeds amount"); break; case "toolbox_bitcoin_invalid_address": console.error("Invalid Bitcoin address format"); break; case "toolbox_bitcoin_utxo_not_found": console.error("Required UTXO not found or spent"); break; case "toolbox_bitcoin_network_error": console.error("Bitcoin network error:", error.cause); break; case "toolbox_bitcoin_transaction_too_large": console.error("Transaction size exceeds limits"); break; default: console.error("Unknown Bitcoin error:", error); } }}Performance Optimization
Section titled “Performance Optimization”UTXO Consolidation
Section titled “UTXO Consolidation”// @noErrorValidation
const consolidateUTXOs = async () => { const address = swapKit.getAddress(Chain.Bitcoin); const utxos = await btcToolbox.getUTXOs(address);
const smallUTXOs = utxos.filter((utxo) => utxo.value < 100000);
if (smallUTXOs.length > 10) { console.log(`Consolidating ${smallUTXOs.length} small UTXOs`);
const consolidationTx = await btcToolbox.transfer({ recipient: address, assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: ( smallUTXOs.reduce((sum, utxo) => sum + utxo.value, 0) / 100000000 ).toString(), }), feeRate: 5, utxos: smallUTXOs, });
console.log("Consolidation transaction:", consolidationTx); }};Fee Optimization
Section titled “Fee Optimization”// @noErrorValidation
const optimizeFee = async ( amount: string, urgency: "low" | "medium" | "high") => { const feeRates = await btcToolbox.getFeeRates();
let selectedFeeRate: number; switch (urgency) { case "low": selectedFeeRate = Math.min(feeRates.average, 10); break; case "medium": selectedFeeRate = feeRates.fast; break; case "high": selectedFeeRate = feeRates.fastest; break; }
const amountSats = parseFloat(amount) * 100000000; if (amountSats > 10000000) { selectedFeeRate = Math.max(selectedFeeRate, feeRates.fast); }
return selectedFeeRate;};
const optimalFeeRate = await optimizeFee("0.05", "medium");const optimizedTx = await btcToolbox.transfer({ recipient: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.05" }), feeRate: optimalFeeRate,});Best Practices
Section titled “Best Practices”-
Use Native SegWit Addresses:
const recipient = "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh";const legacy = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"; -
Monitor Network Congestion:
const feeRates = await btcToolbox.getFeeRates();if (feeRates.fast > 50) {console.warn("Bitcoin network congested, consider waiting");}if (feeRates.average < 20) {console.log("Good time for low-fee transactions");} -
Validate Addresses:
import { validateBitcoinAddress } from "@swapkit/toolboxes/utxo";const isValid = validateBitcoinAddress(recipient);if (!isValid) {throw new Error("Invalid Bitcoin address");} -
Handle Confirmation Times:
const handleConfirmations = (feeRate: number) => {if (feeRate < 5) return "6+ hours";if (feeRate < 20) return "1-6 hours";if (feeRate < 50) return "10-60 minutes";return "10-30 minutes";};const estimatedTime = handleConfirmations(20);console.log(`Expected confirmation time: ${estimatedTime}`);
Address Validation
Section titled “Address Validation”// @noErrorValidationimport { validateBitcoinAddress } from "@swapkit/toolboxes/utxo";
const addresses = [ "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",];
addresses.forEach((address) => { const isValid = validateBitcoinAddress(address); const format = detectAddressFormat(address); console.log(`${address}: ${isValid ? "Valid" : "Invalid"} (${format})`);});
const detectAddressFormat = (address: string): string => { if (address.startsWith("bc1")) return "Native SegWit"; if (address.startsWith("3")) return "SegWit (P2SH)"; if (address.startsWith("1")) return "Legacy"; return "Unknown";};Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”-
Insufficient funds for fee:
const balance = await btcToolbox.getBalance(address);const fee = await btcToolbox.estimateTransactionFee({recipient: "bc1q...",assetValue: AssetValue.from({ chain: Chain.Bitcoin, value: "0.01" }),feeOptionKey: FeeOption.Fast,});if (balance.lte(fee)) {console.error("Insufficient funds to cover transaction fee");} -
Transaction stuck in mempool:
const checkTxStatus = async (txHash: string) => {const tx = await btcToolbox.getTransaction(txHash);if (tx.confirmations === 0) {console.log("Transaction still in mempool");}}; -
Address format mismatch:
const validateCompatibility = (senderFormat: string,recipientFormat: string) => {console.log(`Sending from ${senderFormat} to ${recipientFormat}`);if (senderFormat === "legacy" && recipientFormat === "nativeSegwit") {console.warn("Consider upgrading sender to SegWit for lower fees");}};
API Reference Summary
Section titled “API Reference Summary”Core Methods
Section titled “Core Methods”getBalance()- Get Bitcoin balance in BTCtransfer()- Send Bitcoin with automatic UTXO selectionbuildTransaction()- Construct transaction with custom parametersgetUTXOs()- Fetch unspent transaction outputsgetTransactionHistory()- Get transaction history
Fee Management
Section titled “Fee Management”getFeeRates()- Get current network fee ratesestimateTransactionFee()- Estimate fee for transactionestimateTransactionSize()- Estimate transaction size in bytes
Address Methods
Section titled “Address Methods”getAddress()- Generate address for different formatsvalidateAddress()- Validate Bitcoin address formatcreateMultiSigAddress()- Create multi-signature address
Transaction Methods
Section titled “Transaction Methods”getTransaction()- Get transaction details by hashbroadcastTransaction()- Broadcast signed transactionsignTransaction()- Sign transaction with private key
Next Steps
Section titled “Next Steps”- Learn about other Bitcoin family chains like Bitcoin Cash and Litecoin
- Explore Cross-Chain Swaps using Bitcoin
- Check out THORChain Features for advanced Bitcoin DeFi
- Read about Production Best Practices