Getting Started with Bundle API

The Bundle API is Enso’s multi-action orchestration engine that executes complex DeFi workflows atomically. While the Route API automatically finds optimal paths between positions, the Bundle API gives you precise control over each action in your sequence - perfect for custom strategies, multi-protocol operations, and advanced DeFi automation.

Bundle vs Route: Choose Bundle when you need specific execution order, multi-protocol interactions, or actions beyond simple swaps and deposits. Use Route when you want Enso to handle the optimization automatically.

Atomic execution: All actions in a bundle succeed together or revert entirely - no partial executions or stuck funds.

Quick Start

The Bundle API accepts an array of actions that execute sequentially. Each action can use outputs from previous actions, enabling complex workflows impossible with separate transactions.

Bundle supports 15+ action types across 50+ protocols. Common actions include swap, deposit, borrow, harvest, and bridge. See our Actions Reference for the complete list.

Core Concepts

Action Structure

Each action requires: protocol (where to execute), action (what to do), and args (action-specific parameters). Actions execute in array order.

Dynamic Outputs

Use useOutputOfCallAt to reference previous action outputs. This enables chaining without knowing amounts in advance - critical for harvesting, compounding, and multi-step strategies.

Atomic Safety

All actions succeed or all revert. No partial executions, no stuck funds. Failed actions revert the entire bundle, protecting against incomplete workflows.

Gas Efficiency

One transaction instead of many. Bundle saves 20-40% on gas compared to separate transactions, plus reduced MEV exposure.

Not all actions work for every protocol! To check which actions are supported per protocol, use the SDK’s getActionsBySlug (GET /v1/actions/{protocol}) method.

Sample Workflows

// Harvest rewards → Swap to base token → Redeposit
[
  {
    protocol: "curve-gauge",
    action: "harvest",
    args: {
      token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
      primaryAddress: "0x182B723a58739a9c974cFDB385ceaDb237453c28"
    }
  },
  {
    protocol: "enso",
    action: "route",
    args: {
      tokenIn: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
      tokenOut: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
      amountIn: { useOutputOfCallAt: 0 }, // Use harvested amount
      slippage: "300"
    }
  },
  {
    protocol: "curve",
    action: "deposit",
    args: {
      tokenIn: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
      tokenOut: "0x06325440d014e39736583c165c2963ba99faf14e",
      amountIn: { useOutputOfCallAt: 1 }, // Use swapped amount
      primaryAddress: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"
    }
  }
]

Request Structure

Each action in the bundle array follows this structure:

{
  protocol: string,      // Protocol identifier (e.g., "aave-v3", "uniswap-v2")
  action: string,        // Action type (e.g., "deposit", "swap", "borrow")
  args: {               // Action-specific arguments
    tokenIn?: string,
    tokenOut?: string,
    amountIn?: string | { useOutputOfCallAt: number },
    primaryAddress?: string,
    // ... other protocol-specific args
  }
}

Key parameters:

  • primaryAddress: Protocol’s main contract (pool, vault, router)
  • useOutputOfCallAt: Reference output from action at index N
  • receiver: Optional override for output recipient
  • slippage: Basis points for price protection (100 = 1%)

Examples

0. Install & Authenticate

You can interact with the API using Enso SDK or directly via REST API.

npm i @ensofinance/sdk

To use the API, authenticate using your API key.

import { EnsoClient } from '@ensofinance/sdk';

const ensoClient = new EnsoClient({
  apiKey: 'your-api-key-here'
});

1. Basic Bundle: Swap and Deposit

Start with a simple two-action bundle that swaps ETH for USDC, then deposits into Aave. The first action (route) will invoke Enso’s automatic routing engine and optimize ETH → USDC path,

This simple example is completely achievable with DeFi Swap and Routing. This is for illustrative purposes only.

const bundle = await ensoClient.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate"
  },
  [
    // Step 1: Swap ETH to USDC
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", // ETH
        tokenOut: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
        amountIn: "1000000000000000000", // 1 ETH
        slippage: "100" // 1%
      }
    },
    // Step 2: Deposit USDC to Aave (fixed amount)
    {
      protocol: "aave-v3",
      action: "deposit",
      args: {
        tokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
        tokenOut: "0x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c", // aUSDC
        amountIn: "2000000000", // 2000 USDC
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
      }
    }
  ]
);

// Execute the bundle
await wallet.sendTransaction(bundle.tx);

What’s happening: This bundle swaps 1 ETH for USDC, then deposits exactly 2000 USDC to Aave. Note the fixed amount in the second action - we’ll make this dynamic next.

2. Dynamic Chaining: Harvest and Compound

Use useOutputOfCallAt to chain actions dynamically - essential for harvesting unknown reward amounts.

const bundle = await ensoClient.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate"
  },
  [
    // Step 1: Harvest rewards (unknown amount)
    {
      protocol: "curve-gauge",
      action: "harvest",
      args: {
        token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", // wstETH
        primaryAddress: "0x182B723a58739a9c974cFDB385ceaDb237453c28"
      }
    },
    // Step 2: Swap harvested rewards to stETH
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", // wstETH
        tokenOut: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84", // stETH
        amountIn: { useOutputOfCallAt: 0 }, // Use ALL harvested rewards
        slippage: "300" // 3%
      }
    },
    // Step 3: Deposit back into Curve pool
    {
      protocol: "curve",
      action: "deposit",
      args: {
        tokenIn: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
        tokenOut: "0x06325440d014e39736583c165c2963ba99faf14e",
        amountIn: { useOutputOfCallAt: 1 }, // Use ALL swapped stETH
        primaryAddress: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"
      }
    }
  ]
);

console.log('Compound tx ready:', bundle.tx);

Key concept: useOutputOfCallAt: 0 references the output from the first action (index 0). This enables compound strategies without knowing reward amounts in advance.

4. Leveraged Position

Create a 2x leveraged position by borrowing against deposited collateral.

const bundle = await ensoClient.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate"
  },
  [
    // Deposit WETH as collateral
    {
      protocol: "aave-v3",
      action: "deposit",
      args: {
        tokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH
        tokenOut: "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8", // aWETH
        amountIn: "1000000000000000000", // 1 WETH
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
      }
    },
    // Borrow USDC against WETH
    {
      protocol: "aave-v3",
      action: "borrow",
      args: {
        collateral: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        tokenOut: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
        amountOut: "2000000000", // 2000 USDC
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
      }
    },
    // Swap borrowed USDC back to WETH
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        tokenOut: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        amountIn: { useOutputOfCallAt: 1 }, // All borrowed USDC
        slippage: "100"
      }
    },
    // Deposit the additional WETH
    {
      protocol: "aave-v3",
      action: "deposit",
      args: {
        tokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
        tokenOut: "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8",
        amountIn: { useOutputOfCallAt: 2 }, // All swapped WETH
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2"
      }
    }
  ]
);

console.log('2x leveraged WETH position created');

Leverage mechanics: Deposit → Borrow → Swap → Redeposit creates leveraged exposure. Multiple loops increase leverage further - see our Advanced Leverage Examples.

5. Cross-chain Bundle with Callbacks

Bridge assets and execute actions on the destination chain - all atomically.

What’s happening: The callback array executes atomically on the destination chain. If any callback action fails, the entire operation (including the bridge) reverts - true cross-chain atomicity!

const bundle = await ensoClient.getBundleData(
  {
    chainId: 1, // Ethereum
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate"
  },
  [
    // Convert USDC to ETH for bridge fee
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
        tokenOut: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", // ETH
        amountIn: "3000000000", // 3000 USDC
        slippage: "100"
      }
    },
    // Bridge ETH to Base with callback actions
    {
      protocol: "stargate",
      action: "bridge",
      args: {
        primaryAddress: "0x77b2043768d28e9c9ab44e1abfc95944bce57931",
        destinationChainId: 8453, // Base
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        amountIn: { useOutputOfCallAt: 0 },
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
        // These actions execute on Base after bridging!
        callback: [
          {
            protocol: "enso",
            action: "route",
            args: {
              tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
              tokenOut: "0x2Ae3F1Ec7F328C4243D5eE", // cbETH on Base
              amountIn: { useOutputOfCallAt: 0 }, // Use bridged ETH
              slippage: "200"
            }
          },
          {
            protocol: "aave-v3",
            action: "deposit",
            args: {
              tokenIn: "0x2Ae3F1Ec7F328C4243D5eE", // cbETH
              tokenOut: "0x078f358208685046a11C85e8ad32895DED33A249", // acbETH
              amountIn: { useOutputOfCallAt: 0 }, // From the swap above
              primaryAddress: "0xA238Dd80C259a72e81d7e4664a9801593F98d1c5"
            }
          }
        ]
      }
    }
  ]
);

console.log('Cross-chain deposit ready:', bundle.tx);

Advanced patterns: See our Cross-chain Examples for complex multi-step callbacks, including LP positions and yield strategies.

Utility Actions

Bundle API includes helper actions for safety and flow control:

Balance Check

Get token balance mid-bundle:

{
  "protocol": "enso",
  "action": "balance",
  "args": {
    "token": "0xa0b8...b48"
  }
}

Slippage Protection

Apply slippage to any output:

{
  "protocol": "enso",
  "action": "slippage",
  "args": {
    "amountOut": { "useOutputOfCallAt": 0 },
    "bps": "100"
  }
}

Minimum Output

Enforce minimum amounts:

{
  "protocol": "enso",
  "action": "minAmountOut",
  "args": {
    "amountOut": { "useOutputOfCallAt": 0 },
    "minAmountOut": "1000000000"
  }
}

Fee Collection

Take protocol fees:

{
  "protocol": "enso",
  "action": "fee",
  "args": {
    "token": "0xeee...eee",
    "amount": { "useOutputOfCallAt": 0 },
    "bps": "250",
    "receiver": "0xfee...add"
  }
}

Next Steps

From here, you can explore other resources: