Recursive Bridging

Recursive bridging enables you to execute a multi-leg, cross-chain transfers involving multiple bridges within a single transaction. The primary challenge with multi-bridge routes is paying for gas on the intermediate chains. For example, if you bridge from Arbitrum to Polygon and then want to bridge from Polygon to Avalanche, the second bridge requires MATIC for gas. Recursive bridging solves this by leveraging LayerZero’s ‘native drop’ feature, allowing you to pay for all subsequent gas fees upfront on the origin chain.

Key Concepts

  • Multi-Leg Execution: You can chain complex actions across different networks, such as bridging to a chain, interacting with a protocol, and then bridging out again, all within one atomic transaction.
  • The ‘Native Drop’ Solution: Enso automatically calculates the required gas for “child” bridges and bundles this cost into the “parent” bridge fee using LayerZero’s native drop feature. This fee is paid in the native gas token on the origin chain.

How It Works

At its core, recursive bridging is an advanced bundling strategy. An Enso bundle can include a bridge action whose callback contains another bridge action.
1

Fee Calculation

When you request a route, the Enso API calculates the gas fees required for all “child” bridge calls in the bundle.
2

Parent Bridge Call

The sum of the child bridge fees is passed as the ‘native drop’ value to the initial “parent” bridge call. This cost is included in the overall transaction fee you pay on the origin chain.
3

3. Cross-Chain Execution

The parent bridge completes. On the destination chain, the weiroll script is executed. The “dropped” native tokens are now available to pay the gas fee for the next bridge call in the sequence, ensuring the entire journey completes seamlessly.

Example: Staking on Ethereum from Berachain

A perfect use case for recursive bridging is interacting with a protocol on a chain where your assets are not currently located. Let’s say you have rUSD on Berachain and want to stake it to receive srUSD. The staking protocol (Reservoir) exists on Ethereum. With a single Enso transaction, you can bridge your rUSD from Berachain to Ethereum, stake it for srUSD, and bridge the srUSD back to Berachain. This bundle performs the following steps:
  • Bridge 1: stargate.bridge sends 10 rUSD from Berachain (source) to Ethereum (destination chainId: 1).
  • Action on Ethereum: The callback executes on Ethereum.
    • The mandatory enso.balance action checks the amount of rUSD that arrived.
    • reservoir.deposit stakes the rUSD received from the bridge and mints srUSD, using the dynamic amount from the balance check (useOutputOfCallAt: 0).
  • Bridge 2: stargate.bridge sends the newly acquired srUSD from Ethereum back to Berachain (destination chainId: 80094).
The gas fee for the second bridge (Ethereum to Berachain) is paid in BERA as part of the initial transaction, thanks to the native drop feature. This ensures the entire sequence executes without needing a separate ETH balance on the intermediate wallet.
Use the bundle.gas when sending the transaction from the initial chain. This field contains the simulated value of gas necessary for the entire sequence of transactions:
await sendEOA(bundle.tx, bundle.gas);
When signing with an EOA (using the sendEOA function), you must first execute a separate approval transaction to allow the EnsoRouter contract to spend the token on the origin chain. The approval cannot be included in the recursive bridging bundle and must be performed by the EOA separately before executing the bundle.
import { EnsoClient } from "@ensofinance/sdk";
import { sendEOA } from "toolchain";

async function createRecursiveBridgeWithRouter() {
  const bundle = await client.getBundleData(
    {
      chainId: BERACHAIN_ID,
      fromAddress: USER_ADDRESS,
      routingStrategy: "router",
    },
    [
      // Bridge rUSD from Berachain to Ethereum
      {
        protocol: "stargate",
        action: "bridge",
        args: {
          primaryAddress: STARGATE_RUSD_BRIDGE_ADDR,
          destinationChainId: ETHEREUM_ID,
          tokenIn: RUSD_BERACHAIN_ADDR,
          amountIn: "10000000000000000000",
          receiver: USER_ADDRESS,
          // Callback executes on Ethereum after the bridge completes
          callback: [
            // Balance check for the arrived rUSD on Ethereum
            {
              protocol: "enso",
              action: "balance",
              args: { token: RUSD_ETHEREUM_ADDR },
            },
            // Deposit rUSD into Reservoir to get srUSD
            {
              protocol: "reservoir",
              action: "deposit",
              args: {
                primaryAddress: RESERVOIR_PRIMARY_ADDR,
                tokenIn: RUSD_ETHEREUM_ADDR,
                tokenOut: SRUSD_ETHEREUM_ADDR,
                amountIn: { useOutputOfCallAt: 0 },
              },
            },
            // Bridge 2: srUSD from Ethereum back to Berachain
            {
              protocol: "stargate",
              action: "bridge",
              args: {
                primaryAddress: STARGATE_SRUSD_BRIDGE_ADDR,
                destinationChainId: BERACHAIN_ID,
                tokenIn: SRUSD_ETHEREUM_ADDR,
                amountIn: { useOutputOfCallAt: 1 },
                receiver: USER_ADDRESS,
              },
            },
          ],
        },
      },
    ]
  );
  console.log(
    "Recursive Bridge Bundle with Router:",
    JSON.stringify(bundle, null, 2)
  );

  if (bundle && bundle.tx) {
    // 2. Pass the transaction object and gas estimate to the sending function
    await sendEOA(bundle.tx, bundle.gas);
  } else {
    console.error("Failed to retrieve a valid transaction bundle.");
  }
}


// Initialize the Enso SDK client
const client = new EnsoClient({
  apiKey: "YOUR_API_KEY", // Replace with your Enso API key
});

// Define constants for addresses and chain IDs for clarity
const USER_ADDRESS = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
const BERACHAIN_ID = 80094;
const ETHEREUM_ID = 1;

// Berachain -> Ethereum Bridge (rUSD)
const STARGATE_RUSD_BRIDGE_ADDR = "0x09d4214c03d01f49544c0448dbe3a27f768f2b34";
const RUSD_BERACHAIN_ADDR = "0x09d4214c03d01f49544c0448dbe3a27f768f2b34";
const ENSO_ROUTER_V2_BERACHAIN = "0xF75584eF6673aD213a685a1B58Cc0330B8eA22Cf";

// Actions on Ethereum
const RUSD_ETHEREUM_ADDR = "0x09d4214c03d01f49544c0448dbe3a27f768f2b34";
const RESERVOIR_PRIMARY_ADDR = "0x04716db62c085d9e08050fcf6f7d775a03d07720";
const SRUSD_ETHEREUM_ADDR = "0x738d1115b90efa71ae468f1287fc864775e23a31";

// Ethereum -> Berachain Bridge (srUSD)
const STARGATE_SRUSD_BRIDGE_ADDR = "0x316cd39632cac4f4cdfc21757c4500fe12f64514";

createRecursiveBridgeWithRouter();