BOB Gateway

Bitcoin Wallets

Integrate Bitcoin wallet support for Gateway transactions

Overview

To use BOB Gateway, your application needs to integrate Bitcoin wallet support so users can sign Bitcoin transactions. This guide covers the most popular integration methods.

Reown AppKit (formerly WalletConnect AppKit) provides a unified interface with broad wallet support including Unisat, Leather, Xverse, OKX, and more.

BOB has partnered with Reown to improve Bitcoin wallet support tooling for WalletConnect / Reown.

Installation

npm install @reown/appkit @reown/appkit-adapter-bitcoin

Setup

import { createAppKit } from '@reown/appkit/react';
import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin';
import { bob } from 'viem/chains';

// 1. Get projectId from https://cloud.reown.com
const projectId = 'YOUR_PROJECT_ID';

// 2. Configure Bitcoin networks
const bitcoinAdapter = new BitcoinAdapter({
  networks: [
    {
      chainId: 'bip122:000000000019d6689c085ae165831e93', // Bitcoin mainnet
      name: 'Bitcoin',
      currency: 'BTC',
      explorerUrl: 'https://blockstream.info',
      rpcUrl: 'https://blockstream.info/api',
    },
  ],
});

// 3. Create AppKit instance
const metadata = {
  name: 'Your App Name',
  description: 'Your App Description',
  url: 'https://yourapp.com',
  icons: ['https://yourapp.com/icon.png'],
};

createAppKit({
  adapters: [bitcoinAdapter],
  networks: [bob],
  metadata,
  projectId,
  features: {
    analytics: true,
  },
});

Usage with Gateway SDK

import { useAppKitProvider, useAppKitAccount } from '@reown/appkit/react';
import { ReownWalletAdapter, GatewaySDK } from '@gobob/bob-sdk';
import type { BitcoinConnector } from '@reown/appkit-adapter-bitcoin';

function GatewayComponent() {
  const { walletProvider } = useAppKitProvider<BitcoinConnector>('bip122');
  const { address: btcAddress } = useAppKitAccount();
  
  const gatewaySDK = new GatewaySDK();

  const handleSwap = async () => {
    // Get quote
    const quote = await gatewaySDK.getQuote({
      fromChain: 'bitcoin',
      fromToken: '0x0000000000000000000000000000000000000000',
      fromUserAddress: btcAddress,
      refundAddress: btcAddress, // Bitcoin refund address required for V4 execution
      toChain: 'bob',
      toToken: '0x0555E30da8f98308EdB960aa94C0Db47230d2B9c',
      toUserAddress: evmAddress,
      amount: parseBtc("0.1"),
    });

    // Execute with Reown adapter
    const { order, tx } = await gatewaySDK.executeQuote({
      quote,
      walletClient,
      publicClient,
      btcSigner: new ReownWalletAdapter(walletProvider, btcAddress),
    });
    
    console.log('Order:', order, 'Transaction:', tx);
  };

  return (
    <div>
      <appkit-button />
      <button onClick={handleSwap}>Swap BTC</button>
    </div>
  );
}

Alternative: sats-wagmi

sats-wagmi provides React hooks for Bitcoin wallets with support for Unisat, Leather, Xverse, and more.

Installation

npm install sats-wagmi

Setup

import { SatsWagmiConfig, SatsConnectProvider } from 'sats-wagmi';

function App() {
  return (
    <SatsConnectProvider network="mainnet">
      <SatsWagmiConfig>
        <YourApp />
      </SatsWagmiConfig>
    </SatsConnectProvider>
  );
}

Usage

import { useConnect, useAccount, useSendBitcoin } from 'sats-wagmi';
import { GatewaySDK } from '@gobob/bob-sdk';

function GatewayComponent() {
  const { connect, connectors } = useConnect();
  const { address, isConnected } = useAccount();
  const { sendBitcoin } = useSendBitcoin();

  const handleSwap = async () => {
    if (!isConnected) {
      await connect({ connector: connectors[0] }); // Connect first
    }

    const gatewaySDK = new GatewaySDK();
    
    const quote = await gatewaySDK.getQuote({
      fromChain: 'bitcoin',
      fromToken: '0x0000000000000000000000000000000000000000',
      fromUserAddress: address,
      refundAddress: address, // Bitcoin refund address required for V4 execution
      toChain: 'bob',
      toToken: '0x0555E30da8f98308EdB960aa94C0Db47230d2B9c',
      toUserAddress: evmAddress,
      amount: parseBtc("0.1"),
    });

    // Create custom adapter
    const btcSigner = {
      sendBitcoin: async (params: any) => {
        return sendBitcoin(params);
      },
    };

    const { order, tx } = await gatewaySDK.executeQuote({
      quote,
      walletClient,
      publicClient,
      btcSigner,
    });
  };

  return <button onClick={handleSwap}>Connect & Swap</button>;
}

OKX Wallet

Direct integration with OKX Wallet for users who prefer this wallet.

Installation

npm install @okxweb3/bitcoin-wallet

Usage

import { OkxWalletAdapter, GatewaySDK } from '@gobob/bob-sdk';

function GatewayComponent() {
  const handleSwap = async () => {
    // Check if OKX wallet is installed
    if (!window.okxwallet) {
      alert('Please install OKX Wallet');
      return;
    }

    const gatewaySDK = new GatewaySDK();

    const quote = await gatewaySDK.getQuote({
      fromChain: 'bitcoin',
      fromToken: '0x0000000000000000000000000000000000000000',
      fromUserAddress: btcAddress,
      refundAddress: btcAddress, // Bitcoin refund address required for V4 execution
      toChain: 'bob',
      toToken: '0x0555E30da8f98308EdB960aa94C0Db47230d2B9c',
      toUserAddress: evmAddress,
      amount: parseBtc("0.1"),
    });

    const { order, tx } = await gatewaySDK.executeQuote({
      quote,
      walletClient,
      publicClient,
      btcSigner: new OkxWalletAdapter(window.okxwallet),
    });
  };

  return <button onClick={handleSwap}>Swap with OKX</button>;
}

Custom Bitcoin Signer

Implement the BitcoinSigner interface for any Bitcoin wallet. Return finalized raw transaction hex from either method, not a txid or PSBT. Gateway validates and broadcasts it through V4 register-tx. Prefer a signing-only wallet method; if the wallet broadcasts, retrieve the raw hex before returning (as the built-in OKX adapter does).

import { BitcoinSigner } from '@gobob/bob-sdk';

class CustomWalletAdapter implements BitcoinSigner {
  constructor(private wallet: any) {}

  // Option 1: Implement sendBitcoin for wallets that handle transaction creation
  async sendBitcoin(params: {
    from: string;
    to: string;
    value: string; // BTC amount as string
    opReturn?: string; // OP_RETURN data
  }): Promise<string> {
    // Build and sign without broadcasting; return raw transaction hex
    const txHex = await this.wallet.signBitcoin({
      to: params.to,
      amount: params.value,
      data: params.opReturn,
    });
    
    return txHex; // Return signed transaction hex
  }

  // Option 2: Implement signAllInputs for PSBT-based wallets
  async signAllInputs(psbtHex: string): Promise<string> {
    // Sign the PSBT using your wallet
    const signedPsbt = await this.wallet.signPsbt(psbtHex);
    
    // Finalize and extract transaction
    const finalTx = await this.wallet.finalizePsbt(signedPsbt);
    
    return finalTx; // Return signed transaction hex
  }
}

// Usage
const btcSigner = new CustomWalletAdapter(yourWalletInstance);

const { order, tx } = await gatewaySDK.executeQuote({
  quote,
  walletClient,
  publicClient,
  btcSigner,
});

Dynamic.xyz Integration

Dynamic provides embedded wallet support including Bitcoin.

import { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { GatewaySDK } from '@gobob/bob-sdk';

function GatewayComponent() {
  const { primaryWallet } = useDynamicContext();

  const handleSwap = async () => {
    if (!primaryWallet) return;

    // Get Bitcoin address
    const btcAddress = await primaryWallet.getAddress('bitcoin');

    const gatewaySDK = new GatewaySDK();
    
    const quote = await gatewaySDK.getQuote({
      fromChain: 'bitcoin',
      fromToken: '0x0000000000000000000000000000000000000000',
      fromUserAddress: btcAddress,
      refundAddress: btcAddress, // Bitcoin refund address required for V4 execution
      toChain: 'bob',
      toToken: '0x0555E30da8f98308EdB960aa94C0Db47230d2B9c',
      toUserAddress: evmAddress,
      amount: parseBtc("0.1"),
    });

    // Create adapter for Dynamic wallet
    const btcSigner = {
      sendBitcoin: async (params: any) => {
        return primaryWallet.connector.signTransaction({
          chain: 'bitcoin',
          ...params,
        });
      },
    };

    const { order, tx } = await gatewaySDK.executeQuote({
      quote,
      walletClient,
      publicClient,
      btcSigner,
    });
  };

  return <button onClick={handleSwap}>Swap</button>;
}

Best Practices

Wallet Detection

Detect which wallets are installed and show appropriate connection options

Network Validation

Verify user is on Bitcoin mainnet (or testnet for testing)

Address Validation

Validate Bitcoin addresses before submitting quotes

Error Handling

Handle wallet rejection, insufficient funds, and network errors gracefully

User Feedback

Show clear transaction status and confirmation states

Wallet Comparison

WalletTypeMobileDesktopIntegration Method
Reown AppKitMulti-wallet✅✅Recommended
OKX WalletBrowser Extension✅✅Direct
UnisatBrowser Extension❌✅sats-wagmi
LeatherBrowser Extension❌✅sats-wagmi
XverseMobile + Extension✅✅sats-wagmi
Dynamic.xyzEmbedded✅✅SDK

Testing

Use staging to test your integration against the available routes:

import { GatewaySDK, STAGING_GATEWAY_BASE_URL } from '@gobob/bob-sdk';

const gatewaySDK = new GatewaySDK({ basePath: STAGING_GATEWAY_BASE_URL });
const routes = await gatewaySDK.getRoutes();

Discover chains and tokens from getRoutes() before requesting a quote. Staging is an API environment; do not assume it uses Bitcoin testnet or BOB Sepolia. Use addresses and funds appropriate to the route's actual networks, and include a source-chain refundAddress before execution.

Next Steps

On this page