Skip to main content
This workflow demonstrates bridging tokens using Chainlink’s CCIP protocol with automatic post-bridge execution. CCIP supports larger callback payloads (up to 30KB), making it ideal for complex post-bridge operations. Route Mechanics: This bundle bridges SolvBTC from BNB Chain to Base using CCIP, then automatically swaps it to native ETH:
  • Bridge SolvBTC from BNB Chain to Base using Chainlink CCIP
  • Check SolvBTC balance on Base after successful bridge completion
  • Swap bridged SolvBTC to native ETH using Enso’s routing
Use the GET /ccip/router API to get the CCIP Router address for the primaryAddress parameter.
ccipBridgeWithCallback.ts
import { EnsoClient } from "@ensofinance/sdk";
import { parseUnits } from "viem";

// Chain IDs
const BNB_CHAIN_ID = 56;
const BASE_CHAIN_ID = 8453;

// Common addresses
const WALLET_ADDRESS = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";

// Token addresses
const SOLVBTC_BNB = "0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7";
const SOLVBTC_BASE = "0x3B86Ad95859b6AB773f55f8d94B4b9d443EE931f";
const NATIVE_TOKEN = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";

const client = new EnsoClient({
  apiKey: process.env.ENSO_API_KEY!,
});

// Get CCIP Router address for BNB Chain
const ccipRouter = await client.getCcipRouter({
  chainId: BNB_CHAIN_ID,
});

const bundle = await client.getBundleData(
  {
    chainId: BNB_CHAIN_ID,
    fromAddress: WALLET_ADDRESS,
    routingStrategy: "router",
    receiver: WALLET_ADDRESS,
  },
  [
    {
      protocol: "ccip",
      action: "bridge",
      args: {
        primaryAddress: ccipRouter.router,
        destinationChainId: BASE_CHAIN_ID,
        tokenIn: SOLVBTC_BNB,
        amountIn: "100000000000000", // 0.0001 SolvBTC
        receiver: WALLET_ADDRESS,
        callback: [
          // Step 1: Check SolvBTC balance on Base after bridge
          {
            protocol: "enso",
            action: "balance",
            args: {
              token: SOLVBTC_BASE,
            },
          },
          // Step 2: Swap SolvBTC to native ETH on Base
          {
            protocol: "enso",
            action: "route",
            args: {
              slippage: "100", // 1%
              tokenIn: SOLVBTC_BASE,
              tokenOut: NATIVE_TOKEN,
              amountIn: { useOutputOfCallAt: 0 },
            },
          },
        ],
      },
    },
  ]
);

return bundle;
Important Considerations:
  • CCIP only supports ERC20 tokens - native token bridging is not available
  • Callback data limit is approximately 30KB, allowing for complex post-bridge operations
  • The callback gas limit ranges from 200k (default) to 3M (max)
  • CCIP fees are paid in native token (wrapped)
  • If callback execution fails, tokens are sent to refundReceiver (defaults to receiver)
Finalization timing: CCIP waits for source chain finalization before executing callbacks. This ensures transaction irreversibility but timing varies significantly by chain (sub-second to hours).See Chainlink’s Finality By Blockchain for current finalization times per chain.For time-sensitive swaps with tight slippage protection on slower chains, consider using Relay or Stargate instead.

Without Callback (Simple Transfer)

For simple cross-chain transfers without post-bridge execution:
ccipSimpleTransfer.ts
const bundle = await client.getBundleData(
  {
    chainId: BNB_CHAIN_ID,
    fromAddress: WALLET_ADDRESS,
    routingStrategy: "router",
    receiver: WALLET_ADDRESS,
  },
  [
    {
      protocol: "ccip",
      action: "bridge",
      args: {
        primaryAddress: ccipRouter.router,
        destinationChainId: BASE_CHAIN_ID,
        tokenIn: SOLVBTC_BNB,
        amountIn: "100000000000000",
        receiver: WALLET_ADDRESS,
        // No callback - tokens sent directly to receiver on Base
      },
    },
  ]
);

Resources

Updated