Skip to main content

Overview

BOB Gateway enables users to interact with DeFi protocols using a single Bitcoin transaction. There are three integration approaches:

Custom Strategy

Full control with smart contracts

Multicall

No deployment needed

Cross-Chain

Execute on destination chains

Which Approach Should I Choose?

Choose this when you need:
  • Complex multi-step operations
  • Gas optimization
  • Custom events and logging
  • Full control over execution flow
  • State management in your contract
Choose this when you want to:
  • Integrate with existing contracts on BOB
  • Avoid deploying new contracts
  • Implement simple approve + deposit patterns
  • Get started quickly
Choose this for:
  • Bridging to Ethereum, Base, or other LayerZero chains
  • Executing actions on destination chain after bridge
  • Depositing into lending protocols on destination
  • Multi-step operations post-bridge

Option 1: Custom Strategy Contract

Deploy a smart contract that implements the Gateway strategy interface for full control.

Strategy Interface

interface IStrategy {
    function handleGatewayMessage(
        IERC20 tokenSent,    // Wrapped BTC token (WBTC, tBTC, etc.)
        uint256 amountIn,    // Amount received
        address recipient,   // User's EVM address
        bytes memory message // Optional parameters
    ) external;
}

Complete Example: Convert to SolvBTC

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface ISolvBTCRouter {
    function deposit(
        address targetToken_,
        address currency_,
        uint256 currencyAmount_,
        uint256 minimumTargetTokenAmount_,
        uint64 expireTime_
    ) external returns (uint256);
}

contract SolvBTCStrategy {
    using SafeERC20 for IERC20;

    ISolvBTCRouter public immutable solvBTCRouter;
    IERC20 public immutable solvBTC;

    constructor(address _solvBTCRouter, address _solvBTC) {
        solvBTCRouter = ISolvBTCRouter(_solvBTCRouter);
        solvBTC = IERC20(_solvBTC);
    }

    function handleGatewayMessage(
        IERC20 tokenSent,
        uint256 amountIn,
        address recipient,
        bytes memory message
    ) external {
        // Transfer wrapped BTC from Gateway
        tokenSent.safeTransferFrom(msg.sender, address(this), amountIn);

        // Decode minimum output from message (optional)
        uint256 minOutput = message.length > 0 
            ? abi.decode(message, (uint256)) 
            : 0;

        // Approve SolvBTC router
        tokenSent.safeIncreaseAllowance(address(solvBTCRouter), amountIn);

        // Convert to SolvBTC
        uint256 solvBTCAmount = solvBTCRouter.deposit(
            address(solvBTC), 
            address(tokenSent), 
            amountIn, 
            minOutput,
            uint64(block.timestamp + 1)
        );

        // Send SolvBTC to user
        solvBTC.safeTransfer(recipient, solvBTCAmount);
    }
}

Using Your Custom Strategy

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

const gatewaySDK = new GatewaySDK(bob.id);

// Encode minimum output parameter
const minOutput = parseUnits("0.099", 8); // Minimum 0.099 BTC worth
const message = encodeAbiParameters(
  [{ type: 'uint256' }],
  [minOutput]
);

const quote = await gatewaySDK.getQuote({
  fromChain: 'bitcoin',
  fromToken: 'BTC',
  toChain: 'bob',
  toToken: '0xYourStrategyAddress', // Your deployed strategy
  fromUserAddress: 'bc1q...',
  toUserAddress: '0x...', // Receives the SolvBTC
  amount: parseBtc("0.1"),
  strategyMessage: message, // Optional parameters
});

Option 2: Multicall Strategy

Execute multiple contract calls without deploying custom contracts.

Basic Approve + Deposit Pattern

import { 
  encodeFunctionData, 
  parseAbi, 
  encodeAbiParameters, 
  parseAbiParameters,
  Address 
} from 'viem';
import { GatewaySDK, parseBtc } from '@gobob/bob-sdk';

const WBTC_ADDRESS = '0x03C7054BCB39f7b2e5B2c7AcB37583e32D70Cfa3'; // BOB mainnet
const PROTOCOL_ADDRESS = '0xYourProtocolAddress';

function generateMulticallMessage(
  userAddress: Address, 
  depositAmount: bigint
) {
  // Step 1: Approve protocol to spend WBTC
  const approveCall = encodeFunctionData({
    abi: parseAbi(['function approve(address spender, uint256 value)']),
    functionName: 'approve',
    args: [PROTOCOL_ADDRESS, depositAmount],
  });

  // Step 2: Deposit into protocol
  const depositCall = encodeFunctionData({
    abi: parseAbi(['function deposit(address asset, uint256 amount, address onBehalfOf)']),
    functionName: 'deposit',
    args: [WBTC_ADDRESS, depositAmount, userAddress],
  });

  // Encode as multicall message
  return encodeAbiParameters(
    parseAbiParameters('((address target, bytes callData, uint256 value)[], address fallbackRecipient)'),
    [
      [
        [
          { target: WBTC_ADDRESS, callData: approveCall, value: 0n },
          { target: PROTOCOL_ADDRESS, callData: depositCall, value: 0n },
        ],
        userAddress, // Fallback recipient if execution fails
      ],
    ]
  );
}

// Get quote with multicall
const depositAmount = parseBtc("0.1");
const multicallMessage = generateMulticallMessage(userAddress, depositAmount);

const quote = await gatewaySDK.getQuote({
  fromChain: 'bitcoin',
  fromToken: 'BTC',
  toChain: 'bob',
  toToken: 'wBTC',
  fromUserAddress: 'bc1q...',
  toUserAddress: userAddress,
  amount: depositAmount,
  strategyMessage: multicallMessage,
});

Advanced: Multi-Protocol Interaction

function generateComplexMulticall(user: Address, amount: bigint) {
  const calls = [
    // 1. Approve DEX router
    {
      target: WBTC_ADDRESS,
      callData: encodeFunctionData({
        abi: erc20Abi,
        functionName: 'approve',
        args: [DEX_ROUTER, amount / 2n],
      }),
      value: 0n,
    },
    // 2. Swap half to another token
    {
      target: DEX_ROUTER,
      callData: encodeFunctionData({
        abi: parseAbi(['function swapExactTokensForTokens(...)']),
        functionName: 'swapExactTokensForTokens',
        args: [/* swap params */],
      }),
      value: 0n,
    },
    // 3. Add liquidity
    {
      target: DEX_ROUTER,
      callData: encodeFunctionData({
        abi: parseAbi(['function addLiquidity(...)']),
        functionName: 'addLiquidity',
        args: [/* liquidity params */],
      }),
      value: 0n,
    },
  ];

  return encodeAbiParameters(
    parseAbiParameters('((address target, bytes callData, uint256 value)[], address fallbackRecipient)'),
    [[calls, user]]
  );
}

Option 3: Cross-Chain Destination Calls

Coming Soon - This feature is currently under development and will be available soon.

Next Steps