Crosschain routing enables complex multichain workflows that execute DeFi operations across multiple blockchains atomically. Unlike simple token transfers, crosschain routing orchestrates sophisticated strategies that span several chains. Both route and bundle API operate in crosschain mode. For custom bundles, use the bridge action to facilitate cross-chain token transfers using Stargate. For example: bridge assets to chains where protocols exist, execute operations such as minting, then optionally bridge results back to your origin chain and deposit them in a yield-bearing position.

Quick Start

Simple cross-chain token transfer Try this route → To simply bridge an asset, use the route API with automatic route optimization from input and output token/chain pairs.
SDK
// Basic USDC transfer: Ethereum → Base
const route = await ensoClient.getRouteData({
  fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  chainId: 1, // Ethereum
  destinationChainId: 8453, // Base
  tokenIn: ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], // USDC Ethereum
  tokenOut: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"], // USDC Base
  amountIn: ["1000000000"], // 1,000 USDC
  slippage: "300", // 3%
  routingStrategy: "delegate"
});

await wallet.sendTransaction(route.tx);

Core Concepts

Bridging

Crosschain routing uses Stargate and LayerZero for bridging assets.

The bridge action’s parameter primaryAddress must reference an appropriate pool contract, exposed by the layerZero/pool API.

Native Drop

Parent bridge calls calculate gas fees required for all child bridge operations and include these costs in the initial transaction fee using LayerZero’s native drop feature.

Post-Bridge Execution

Callback arrays of Enso Actions execute on the destination chain after bridge completion. All actions execute atomically.

When to use Route vs Bundle API?

Use Route API for: Limitations: Cannot handle custom post-bridge logic or multi-step protocols interactions Use Bundle API for:

Examples

1. Simple Cross-Chain Swap

Use Route API for basic cross-chain operations with automatic pathfinding.
SDK
// ETH on Ethereum → USDC on Base
const route = await ensoClient.getRouteData({
  fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  chainId: 1, // Ethereum
  destinationChainId: 8453, // Base
  tokenIn: ["0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"], // ETH
  tokenOut: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"], // USDC Base
  amountIn: ["1000000000000000000"], // 1 ETH
  slippage: "300", // 3%
  routingStrategy: "delegate"
});

2. Crosschain Vault Zap

In this example, we’ll bridge ETH from Ethereum to zap it to a Ether.fi weETH vault on Base. Try this route →
const route = await ensoClient.getRouteData({
  fromAddress: "0x...",
  chainId: 1, // Ethereum
  destinationChainId: 8453, // Base
  tokenIn: ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], // USDC Ethereum
  tokenOut: ["0x04C0599Ae5A44757c0af6F9eC3b93da8976c150A"], // weETH vault on Base
  amountIn: ["1000000000"], // 1,000 USDC
  slippage: "500", // 5% (complex operation)
  routingStrategy: "delegate"
});

// Bridge + vault entry in one transaction
await wallet.sendTransaction(route.tx);

3. Cross-Chain Position Minting

Crosschain routing enables you to mint positions on different chains and bridge them back. In this example, we’ll bridge USDC from Berachain to Ethereum, mint e-rUSD using Reservoir protocol, then bridge rUSD back to Berachain. About Reservoir: Reservoir is a stablecoin protocol that mints rUSD (a USD-pegged stablecoin) by accepting USDC as collateral on Ethereum mainnet.
Use the GET layerzero/pool API to find the correct pool address and use it as the primaryAddress for stargate.bridge operation.
What’s happening: This workflow demonstrates a complete round-trip bridge operation - taking USDC from Berachain, minting a stablecoin on Ethereum where the protocol exists, then bringing the newly minted e-rUSD back to the origin chain.
Understanding callbacks executionCallbacks execute on the destination chain after the bridge completes, but they’re not separate transactions. The bridge operation includes encoded instructions that execute atomically using Enso’s crosschain execution engine. This means:
  • Atomic Safety: If any callback action fails, the entire bridge operation reverts. Your funds never get stuck in an intermediate state between chains.
  • Gas Management: The initial transaction on the source chain calculates and pays for all destination chain gas costs using LayerZero’s native drop feature. You don’t need to hold native tokens on every destination chain.
  • Output Chaining: Callbacks can reference outputs from previous callback actions using useOutputOfCallAt, enabling complex multi-step workflows that adapt to actual bridged amounts rather than fixed values.
mintOnBeraFromMainnet.ts
const bundle = await client.getBundleData(
  {
    chainId: BERACHAIN_ID,
    fromAddress: WALLET_ADDRESS,
    spender: WALLET_ADDRESS,
    routingStrategy: "router",
  },
  [
    {
      protocol: "stargate",
      action: "bridge",
      args: {
        primaryAddress: STARGATE_USDC_BRIDGE,
        destinationChainId: ETHEREUM_ID,
        tokenIn: USDC_BERACHAIN,
        amountIn: parseUnits("1000", 6).toString(), // 1000 USDC
        receiver: WALLET_ADDRESS,
        callback: [ // on Ethereum
          // Step 1: Check USDC balance on Ethereum after bridge
          {
            protocol: "enso",
            action: "balance",
            args: {
              token: USDC_ETHEREUM,
            },
          },
          // Step 2: Mint e-rUSD using bridged USDC on Ethereum
          {
            protocol: "enso",
            action: "deposit",
            args: {
              primaryAddress: RESERVOIR_MINTING_CONTRACT,
              tokenIn: USDC_ETHEREUM,
              tokenOut: RUSD_ETHEREUM,
              amountIn: { useOutputOfCallAt: 0 }, // Use USDC from balance check
              receiver: WALLET_ADDRESS,
            },
          },
          // Step 3: Bridge newly minted e-rUSD back to Berachain
          {
            protocol: "stargate",
            action: "bridge",
            args: {
              primaryAddress: STARGATE_E_RUSD_BRIDGE,
              destinationChainId: BERACHAIN_ID,
              tokenIn: RUSD_ETHEREUM,
              amountIn: { useOutputOfCallAt: 1 }, // Use e-rUSD from minting
              receiver: WALLET_ADDRESS
            },
          },
        ],
      },
    },
  ]
);

const BERACHAIN_ID = 80094;
const ETHEREUM_ID = 1;

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

// Token addresses
const USDC_BERACHAIN = "0x549943e04f40284185054145c6E4e9568C1D3241";
const USDC_ETHEREUM = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const RUSD_ETHEREUM = "0x09D4214C03D01F49544C0448DBE3A27f768F2b34";
const RUSD_BERACHAIN = "0x09D4214C03D01F49544C0448DBE3A27f768F2b34";

// Protocol addresses
const RESERVOIR_MINTING_CONTRACT = "0x4809010926aec940b550D34a46A52739f996D75D";
const STARGATE_USDC_BRIDGE = "0xAF54BE5B6eEc24d6BFACf1cce4eaF680A8239398";
const STARGATE_E_RUSD_BRIDGE = "0xf0e9f6d9ba5d1b3f76e0f82f9dcdb9ebeef4b4da";

// Euler addresses on Berachain
const EULER_VAULT_E_RUSD_BERACHAIN = "0x109D6D1799f62216B4a7b0c6e245844AbD4DD281";

4. Crosschain Yield Strategy

Nested callbacks enable multi-hop workflows, allowing operations that span multiple chains where different protocols exist. In this example, we’ll do an Euler deposit of rUSD tokens minted on a different chain. The user starts with USDC on Berachain and ends with yield-generating vault shares in a single transaction. Bridge USDC from Berachain to Ethereum, mint e-rUSD using Reservoir protocol, bridge e-rUSD back to Berachain, then deposit into Euler vault.
mintOnBeraDepositOnMainnet.ts
const BERACHAIN_ID = 80094;
const ETHEREUM_ID = 1;

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

// Token addresses
const USDC_BERACHAIN = "0x549943e04f40284185054145c6E4e9568C1D3241";
const USDC_ETHEREUM = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const RUSD_ETHEREUM = "0x09D4214C03D01F49544C0448DBE3A27f768F2b34";
const RUSD_BERACHAIN = "0x09D4214C03D01F49544C0448DBE3A27f768F2b34";

// Protocol addresses
const RESERVOIR_MINTING_CONTRACT = "0x4809010926aec940b550D34a46A52739f996D75D";
const STARGATE_USDC_BRIDGE = "0xAF54BE5B6eEc24d6BFACf1cce4eaF680A8239398";
const STARGATE_E_RUSD_BRIDGE = "0xf0e9f6d9ba5d1b3f76e0f82f9dcdb9ebeef4b4da";

// Euler addresses on Berachain
const EULER_VAULT_E_RUSD_BERACHAIN = "0x109D6D1799f62216B4a7b0c6e245844AbD4DD281";

const bundle = await client.getBundleData(
  {
    chainId: BERACHAIN_ID,
    fromAddress: WALLET_ADDRESS,
    spender: WALLET_ADDRESS,
    routingStrategy: "router",
  },
  [
    {
      protocol: "stargate",
      action: "bridge",
      args: {
        primaryAddress: STARGATE_USDC_BRIDGE,
        destinationChainId: ETHEREUM_ID,
        tokenIn: USDC_BERACHAIN,
        amountIn: parseUnits("1000", 6).toString(), // 1000 USDC
        receiver: WALLET_ADDRESS,
        callback: [
          // Step 1: Check USDC balance on Ethereum after bridge
          {
            protocol: "enso",
            action: "balance",
            args: {
              token: USDC_ETHEREUM,
            },
          },
          // Step 2: Mint e-rUSD using bridged USDC on Ethereum
          {
            protocol: "reservoir",
            action: "deposit",
            args: {
              primaryAddress: RESERVOIR_MINTING_CONTRACT,
              tokenIn: USDC_ETHEREUM,
              tokenOut: RUSD_ETHEREUM,
              amountIn: { useOutputOfCallAt: 0 }, // Use USDC from balance check
              receiver: WALLET_ADDRESS,
            },
          },
          // Step 3: Bridge newly minted e-rUSD back to Berachain
          {
            protocol: "stargate",
            action: "bridge",
            args: {
              primaryAddress: STARGATE_E_RUSD_BRIDGE,
              destinationChainId: BERACHAIN_ID,
              tokenIn: RUSD_ETHEREUM,
              amountIn: { useOutputOfCallAt: 1 }, // Use e-rUSD from minting
              receiver: WALLET_ADDRESS,
              // Nested callback executes on Berachain after e-rUSD arrives
              callback: [
                // Step 4: Check e-rUSD balance on Berachain
                {
                  protocol: "enso",
                  action: "balance",
                  args: {
                    token: RUSD_BERACHAIN,
                  },
                },
                // Step 5: Deposit e-rUSD into Euler vault on Berachain
                {
                  protocol: "euler-v2",
                  action: "deposit",
                  args: {
                    primaryAddress: EULER_VAULT_E_RUSD_BERACHAIN,
                    tokenIn: RUSD_BERACHAIN,
                    tokenOut: EULER_VAULT_E_RUSD_BERACHAIN, // ERC4626 vault token
                    amountIn: { useOutputOfCallAt: 0 }, // Use e-rUSD from balance check
                    receiver: WALLET_ADDRESS,
                  },
                },
              ],
            },
          },
        ],
      },
    },
  ]
);

Reference

Bridge Action Parameters

ParameterDescriptionRequired
primaryAddressStargate pool contract address for the source chainYes
destinationChainIdTarget blockchain network IDYes
tokenInToken address to bridge from source chainYes
amountInAmount to bridge (with full decimals) or reference to previous action outputYes
receiverAddress to receive bridged tokens on destination chainYes
callbackArray of actions to execute on destination chain after bridgingNo

Callback Requirements

Critical: All callback sequences must begin with a balance check action to verify the bridged token amount on the destination chain.
  • First action must be: { protocol: "enso", action: "balance", args: { token: "bridged_token_address" } }
  • Reference previous outputs: Use { useOutputOfCallAt: index } to chain callback actions
  • Atomic execution: All callback actions succeed or the entire bridge operation reverts
  • Nested callbacks: Bridge actions within callbacks enable multi-hop workflows

Supported Chains and Tokens

Use the GET layerzero/pool API to find the correct pool address and use it as the primaryAddress for stargate.bridge operation.

Updated