> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enso.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Bundle Actions Reference

> Public actions, inputs, outputs, and composition rules for Bundle API requests.

export const date_0 = "2026-03-12"

The Bundle API accepts an ordered array of public actions. This page documents stable Enso composition actions and common protocol action families.

<Note>
  Start with the [Bundle API guide](/pages/build/get-started/bundling-actions) for request shape, execution, approvals, and response handling. Use the standalone [Route API](/pages/build/get-started/route) when Enso should choose the complete path. The `enso:route` section below documents the route action used inside a Bundle request.
</Note>

<Info>
  Protocol support and inputs change over time. Use [Supported Actions](/api-reference/integration/supported-actions) for the current action list and [Standard per Protocol](/api-reference/integration/standard-per-protocol) for actions available on a specific protocol. Generic protocol tables below describe common fields, not a universal schema for every integration.
</Info>

## Action index

* **Routing and composition:** [`route`](#route), [`split`](#split), [`merge`](#merge), [`call`](#call)
* **Safety and fees:** [`slippage`](#slippage), [`minamountout`](#minamountout), [`fee`](#fee), [`ensofee`](#ensofee)
* **Protocol actions:** [`swap`](#swap), [`deposit`](#deposit), [`redeem`](#redeem), [`bridge`](#bridge), [`borrow`](#borrow), [`repay`](#repay), [`harvest`](#harvest)
* **Token actions:** [`approve`](#approve), [`transfer`](#transfer), [`transferfrom`](#transferfrom), [`permittransferfrom`](#permittransferfrom), [`balance`](#balance)
* **Liquidity actions:** [`depositclmm`](#depositclmm), [`redeemclmm`](#redeemclmm)
* **Logic:** [Math Actions](#math-actions), [Helpers Actions](#helpers-actions)

## Chaining actions

Use a previous output as a later input with a zero-based reference:

```typescript theme={null}
{ useOutputOfCallAt: 0 }
```

Array outputs require an explicit item index:

```typescript theme={null}
{ useOutputOfCallAt: 0, index: 1 }
```

The following source-backed example splits USDT into two routed outputs and passes both into a CLMM deposit:

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 42161,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "router",
  },
  [
    {
      protocol: "enso",
      action: "split",
      args: {
        tokenIn: "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9",
        tokenOut: [
          "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
          "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9",
        ],
        amountIn: "100000000",
      },
    },
    {
      protocol: "sushiswap-v3",
      action: "depositclmm",
      args: {
        tokenOut: "0xf0cbce1942a68beb3d1b73f0dd86c8dcc363ef49",
        ticks: [-887271, 887271],
        tokenIn: [
          "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
          "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9",
        ],
        poolFee: "100",
        amountIn: [
          { useOutputOfCallAt: 0, index: 0 },
          { useOutputOfCallAt: 0, index: 1 },
        ],
      },
    },
  ],
);
```

References must point to an earlier available output. Checks such as `check` and `minamountout` do not produce a normal token amount for downstream consumption.

## Action outputs

| Action            | Output                                         | Shape           | Reference notes                                                      |
| ----------------- | ---------------------------------------------- | --------------- | -------------------------------------------------------------------- |
| `route`           | Routed token amount or amounts                 | Scalar or array | Use `index` for a multi-output route.                                |
| `balance`         | Execution-account or requested account balance | Scalar          | Advanced `account` and `estimate` behavior is documented separately. |
| `split`           | One routed amount per `tokenOut`               | Array           | `index` is required.                                                 |
| `merge`           | Merged output amount                           | Scalar          | —                                                                    |
| `call`            | Contract return value                          | ABI-dependent   | Shape follows the supplied ABI.                                      |
| `slippage`        | Calculated minimum amount                      | Scalar          | The referenced producer must already exist.                          |
| `minamountout`    | Assertion only                                 | None            | Do not reference it as a token producer.                             |
| `fee`, `ensofee`  | Amount after fee                               | Scalar          | Feed the net amount into the next action.                            |
| Math actions      | Calculated number                              | Scalar          | —                                                                    |
| Comparison, `not` | Boolean result                                 | Scalar          | —                                                                    |
| `toggle`          | Selected amount                                | Scalar          | —                                                                    |
| `check`           | Assertion only                                 | None            | Reverts when the condition is false.                                 |
| Protocol actions  | Protocol-specific                              | Varies          | Use current per-protocol action metadata.                            |

## Ondo quote estimates

`GET /api/v1/shortcuts/quote` supports routes containing the synthetic `ondosplit` and `ondomerge` actions. It returns a soft estimate based on current Folio view data and Ondo soft attestations; it does not build an executable Ondo transaction or obtain a hard attestation. Use the Route API when execution-ready Ondo actions are required.

Generic `split` and `merge` actions remain unavailable in the Quote API.

## Core actions

### `route`

`enso:route` finds a path between `tokenIn` and `tokenOut` inside a Bundle request. It can expand into swaps, deposits, or redeems, and the Bundle response includes the selected hops in `route`.

This is different from the standalone [Route API](/pages/build/get-started/route), which is usually simpler when the entire request has one final destination.

```typescript theme={null}
{
  protocol: "enso",
  action: "route",
  args: {
    tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    tokenOut: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
    amountIn: "1000000000000000000",
    slippage: "300",
    fee: ["100"],
    feeReceiver: "0x220866B1A2219f40e72f5c628B65D54268cA3A9D",
  },
}
```

Route fees are deducted from the corresponding input amounts before routing. The `fee` array aligns with `amountIn`; `"100"` is 1%.

For cross-chain routing, add `destinationChainId`. `ignoreBridges` and `refundReceiver` apply only to cross-chain route actions. Source completion and destination execution are asynchronous.

```typescript theme={null}
{
  protocol: "enso",
  action: "route",
  args: {
    tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    tokenOut: "0x94b008aa00579c1307b0ef2c499ad98a8ce58e58",
    amountIn: "1000000000000000000",
    destinationChainId: 10,
    receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    refundReceiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    ignoreBridges: ["ccip"],
  },
}
```

| Parameter            | Description                                                                                 | Required |
| -------------------- | ------------------------------------------------------------------------------------------- | -------- |
| `tokenIn`            | Input token address. Use `0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee` for the native asset. | Yes      |
| `tokenOut`           | Output token address.                                                                       | Yes      |
| `amountIn`           | Base-unit amount or previous output reference.                                              | Yes      |
| `slippage`           | Slippage tolerance in basis points.                                                         | No       |
| `minAmountOut`       | Explicit minimum output amount or amounts.                                                  | No       |
| `receiver`           | Output recipient override.                                                                  | No       |
| `fee`                | Fee BPS values aligned with the input amounts.                                              | No       |
| `feeReceiver`        | Address receiving route fees. Required when `fee` is set.                                   | No       |
| `ignoreAggregators`  | Aggregators to exclude from route selection.                                                | No       |
| `ignoreStandards`    | Protocol standards to exclude from route selection.                                         | No       |
| `destinationChainId` | Destination chain for a cross-chain route.                                                  | No       |
| `refundReceiver`     | Cross-chain dust/refund recipient.                                                          | No       |
| `ignoreBridges`      | Bridges to exclude from cross-chain route selection.                                        | No       |

### `swap`

Swaps one token for another using a specific pool or exchange protocol.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "uniswap-v2",
      action: "swap",
      args: {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        amountIn: "1000000000000000000", // 1 WETH (18 decimals)
        primaryAddress: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", // Uniswap V2 Router
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Receiver
        slippage: "100", // Optional: 1% slippage (100 basis points)
        poolFee: "3000", // Optional: Pool fee in basis points (e.g., 3000 for 0.3%)
      },
    },
  ],
);
```

| Parameter        | Description                                                 | Required |
| ---------------- | ----------------------------------------------------------- | -------- |
| `tokenIn`        | Address of token to swap from                               | Yes      |
| `tokenOut`       | Address of token to swap to                                 | Yes      |
| `amountIn`       | Amount of `tokenIn` to swap in wei (with full decimals)     | Yes      |
| `primaryAddress` | Address of the router or pool contract                      | No       |
| `receiver`       | Address to receive the output tokens                        | Yes      |
| `slippage`       | Slippage tolerance in basis points (100 = 1%)               | No       |
| `poolFee`        | Optional pool fee in basis points when using specific pools | No       |
| `poolId`         | The ID to identify the liquidity pool                       | No       |

### `deposit`

Deposits tokens into a protocol to receive a position token or add liquidity.

<Tabs>
  <Tab title="Morpho">
    Use the optional `positionId` to deposit into.

    ```js theme={null}
    {
      protocol: "morpho-markets-v1",
      action: "deposit",
      args: {
        amountIn: { useOutputOfCallAt: 0 },
        tokenIn: TOKEN_IN_ADDRESS,
        receiver: WALLET_ADDRESS,
        primaryAddress: MORPHO,
        positionId: MORPHO_POSITION_ID,
      },
    }
    ```
  </Tab>

  <Tab title="Aave">
    ```js theme={null}
    const bundle = await client.getBundleData(
      {
        chainId: 1,
        fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
        routingStrategy: "delegate",
      },
      [
        {
          protocol: "aave-v3",
          action: "deposit",
          args: {
            tokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address
            tokenOut: "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8", // aWETH address (optional)
            amountIn: "1000000000000000000", // Amount in wei (1 WETH)
            primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", // Aave V3 pool
            receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver address
          },
        },
      ],
    );
    ```
  </Tab>
</Tabs>

| Parameter        | Description                                                                                                                           | Required |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `tokenIn`        | Address of token to deposit. Can be a single address or an array for multiple tokens                                                  | Yes      |
| `tokenOut`       | Address of token to receive. Can be a single address or an array for multiple tokens. Omit if position is not represented by a token. | No       |
| `amountIn`       | Amount of `tokenIn` to deposit in wei (with full decimals). Can be a single value or an array for multiple tokens                     | Yes      |
| `primaryAddress` | Address of the protocol contract to interact with                                                                                     | Yes      |
| `receiver`       | Address to receive the output tokens if not the caller                                                                                | No       |
| `positionId`     | Position ID to deposit into (e.g. Morpho Markets' positions)                                                                          | No       |

### `redeem`

Redeems underlying assets from a protocol by exchanging shares or tokens. Use it to exit a position and retrieve the desired tokens.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    // Step 1: First deposit into the ERC4626 vault to get shares
    {
      protocol: "erc4626",
      action: "deposit",
      args: {
        tokenIn: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI (underlying asset)
        tokenOut: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // yvDAI (vault shares)
        amountIn: "1000000000000000000", // 1 DAI
        primaryAddress: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // Vault address
      },
    },
    // Step 2: Now redeem the shares we just received
    {
      protocol: "erc4626",
      action: "redeem",
      args: {
        tokenIn: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // yvDAI shares
        tokenOut: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI (underlying asset)
        amountIn: { useOutputOfCallAt: 0 }, // Use the shares from the deposit
        primaryAddress: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // Vault address
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver
      },
    },
  ],
);
```

| Parameter        | Description                                                                         | Required |
| ---------------- | ----------------------------------------------------------------------------------- | -------- |
| `tokenIn`        | Address of shares/tokens to redeem                                                  | No       |
| `tokenOut`       | Address of token(s) to receive upon redemption. Can be a single address or an array | Yes      |
| `amountIn`       | Amount of shares/tokens to redeem in wei (with full decimals)                       | Yes      |
| `primaryAddress` | Address of the contract to interact with                                            | Yes      |
| `receiver`       | Address to receive the output tokens if not the caller                              | No       |

## Bridging

The `bridge` action lets you perform transactions spanning multiple chains.
It relies on Stargate and LayerZero to coordinate interactions.

### `bridge`

Facilitates cross-chain token transfers using various bridge protocols with optional callback actions on the destination chain.

When used with callback actions (`callback` array), it can execute a destination-chain sequence after tokens arrive. Source bridging and destination execution are asynchronous; callback failure cannot revert a finalized source-chain bridge. Monitor the transfer through [Bridge Status](/pages/build/get-started/bridge-status).

<Note>
  **Important**: When using callbacks, the callback bundle MUST start with a
  balance action to check the bridged token balance on the destination chain.
</Note>

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
    spender: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
    routingStrategy: "router",
  },
  [
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC on mainnet
        amountIn: "1000000000", // 1000 USDC
        tokenOut: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", // ETH
      },
    },
    {
      protocol: "enso",
      action: "fee",
      args: {
        token: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        amount: { useOutputOfCallAt: 0 },
        bps: 25,
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11", // Fee receiver
      },
    },
    {
      protocol: "stargate",
      action: "bridge",
      args: {
        primaryAddress: "0x77b2043768d28e9c9ab44e1abfc95944bce57931",
        destinationChainId: 8453,
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        amountIn: { useOutputOfCallAt: 1 },
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
        callback: [
          {
            protocol: "enso",
            action: "balance",
            args: {
              token: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
            },
          },
          {
            protocol: "enso",
            action: "split",
            args: {
              tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
              tokenOut: [
                "0x50c5725949a6f0c72e6c4a641f24049a917db0cb",
                "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
              ],
              amountIn: { useOutputOfCallAt: 0 },
            },
          },
          {
            protocol: "enso",
            action: "slippage",
            args: {
              amountOut: { useOutputOfCallAt: 1, index: 0 },
              bps: 50,
            },
          },
          {
            protocol: "enso",
            action: "slippage",
            args: {
              amountOut: { useOutputOfCallAt: 1, index: 1 },
              bps: 50,
            },
          },
          {
            protocol: "uniswap-v4",
            action: "depositclmm",
            args: {
              tokenOut: "0x7c5f5a4bbd8fd63184577525326123b519429bdc",
              ticks: [-276842, -275842],
              tokenIn: [
                "0x50c5725949a6f0c72e6c4a641f24049a917db0cb",
                "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
              ],
              poolFee: "100",
              amountIn: [
                { useOutputOfCallAt: 1, index: 0 },
                { useOutputOfCallAt: 1, index: 1 },
              ],
            },
          },
        ],
      },
    },
  ],
);
```

| Parameter            | Description                                                                                          | Required |
| -------------------- | ---------------------------------------------------------------------------------------------------- | -------- |
| `primaryAddress`     | Source bridge pool address                                                                           | Yes      |
| `destinationChainId` | Target blockchain network ID                                                                         | Yes      |
| `tokenIn`            | Token address to bridge from source chain                                                            | Yes      |
| `amountIn`           | Amount to bridge (with full decimals) or a reference to a previous action's output                   | Yes      |
| `receiver`           | Address to receive bridged tokens on destination chain                                               | Yes      |
| `callback`           | Array of actions to execute on the destination chain after bridging (must start with balance action) | No       |
| `callbackValue`      | Additional value passed to the callback (in addition to bridge token)                                | No       |
| `protocolArgs`       | Provider-specific options such as CCTP transfer type and forward-fee level                           | No       |

Bridge providers do not all support the same callback shapes. Use [Crosschain Routing](/pages/build/get-started/crosschain-routing) for provider-specific constraints.

## Slippage protection

The `route` action accepts its own `slippage` or explicit minimum settings. For direct protocol actions and internal routes used by composition actions, add `slippage` or `minamountout` checks where the flow requires explicit protection.

### `slippage`

Derives a quote-backed minimum for a previously produced output and adds an on-chain check. The transaction reverts when the received amount is below that minimum.

The referenced output must already exist. In `split -> slippage -> depositclmm` flows, the slippage checks intentionally sit before `depositclmm` because they protect the internal routes that prepare its inputs.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "uniswap-v2",
      action: "swap",
      args: {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amountIn: "1000000000000000000",
        primaryAddress: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
      },
    },
    {
      protocol: "enso",
      action: "slippage",
      args: {
        bps: "100",
        amountOut: { useOutputOfCallAt: 0 },
      },
    },
  ],
);
```

| Parameter   | Description                                                                                | Required |
| ----------- | ------------------------------------------------------------------------------------------ | -------- |
| `amountOut` | Expected output amount - **must** be output of one of previous actions `useOutputOfCallAt` | Yes      |
| `bps`       | Maximum acceptable slippage in basis points (1 bps = 0.01%, 100 bps = 1%)                  | Yes      |

### `minAmountOut`

Applies slippage protection specified in absolute amount (`minAmountOut`) to ensure the received amount is within the acceptable range of the expected output.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    // First action to get an output
    {
      protocol: "uniswap-v2",
      action: "swap",
      args: {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        amountIn: "1000000000000000000", // 1 WETH
        primaryAddress: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", // Uniswap V2 Router
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      },
    },
    // Now apply minAmountOut check
    {
      protocol: "enso",
      action: "minamountout",
      args: {
        amountOut: { useOutputOfCallAt: 0 }, // Reference to first action's output
        minAmountOut: "1940000", // Minimum amount (1.94 USDC)
      },
    },
  ],
);
```

| Parameter      | Description                                                                                | Required |
| -------------- | ------------------------------------------------------------------------------------------ | -------- |
| `amountOut`    | Expected output amount - **must** be output of one of previous actions `useOutputOfCallAt` | Yes      |
| `minAmountOut` | Minimum acceptable amount of out token                                                     | Yes      |

`minamountout` is an assertion-only action. Do not use its position as a downstream token-amount reference.

## Deposit & Redeem Actions

### `depositCLMM`

Deposits tokens into a Concentrated Liquidity Market Maker pool (like Uniswap V3).

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
    spender: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
    routingStrategy: "router",
  },
  [
    {
      protocol: "uniswap-v4",
      action: "depositclmm",
      args: {
        tokenOut: "0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e",
        ticks: [-887270, 887270],
        tokenIn: [
          "0xdac17f958d2ee523a2206206994597c13d831ec7", // USDT
          "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", // WBTC
        ],
        poolFee: "500",
        amountIn: ["1000000000", "100000000"], // 1000 USDT (6 decimals), 1 WBTC (8 decimals)
      },
    },
  ],
);
```

| Parameter     | Description                                                                                       | Required |
| ------------- | ------------------------------------------------------------------------------------------------- | -------- |
| `tokenIn`     | Array of addresses of tokens to deposit (usually 2 tokens)                                        | Yes      |
| `tokenOut`    | Address of the position NFT to receive                                                            | Yes      |
| `amountIn`    | Array of amounts to deposit in wei (with full decimals). Must match the length of `tokenIn` array | Yes      |
| `ticks`       | Array containing lower and upper tick bounds defining the price range                             | Yes      |
| `poolFee`     | Pool fee tier in basis points (e.g., 3000 for 0.3%)                                               | Yes      |
| `receiver`    | Address to receive the position NFT if not the caller                                             | No       |
| `tickSpacing` | The minimum gap between valid price points for adding liquidity                                   | No       |
| `hook`        | The hook address for Uniswap V4 pools                                                             | No       |

### `redeemCLMM`

Redeems tokens from a Concentrated Liquidity Market Maker position (like Uniswap V3).

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "uniswap-v3",
      action: "redeemclmm",
      args: {
        tokenIn: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", // UNI-V3-POS NFT
        tokenOut: [
          "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
          "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        ],
        liquidity: "1000000000000", // Liquidity amount to withdraw
        tokenId: "123456", // The NFT token ID
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver
      },
    },
  ],
);
```

| Parameter   | Description                                                | Required |
| ----------- | ---------------------------------------------------------- | -------- |
| `tokenIn`   | Address of the position NFT token                          | Yes      |
| `tokenOut`  | Array of addresses of tokens to receive                    | Yes      |
| `liquidity` | Amount of liquidity to withdraw                            | Yes      |
| `tokenId`   | ID of the position NFT                                     | Yes      |
| `receiver`  | Address to receive the underlying tokens if not the caller | No       |

### `singleDeposit`

Deposits a single token into a protocol. This is a specialized version of the `deposit` action for single token deposits.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "yearn",
      action: "singledeposit",
      args: {
        tokenIn: "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI address
        tokenOut: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // yvDAI address
        amountIn: "10000000000000000000", // 10 DAI (18 decimals)
        primaryAddress: "0xdA816459F1AB5631232FE5e97a05BBBb94970c95", // Yearn vault
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver
      },
    },
  ],
);
```

| Parameter        | Description                                                        | Required |
| ---------------- | ------------------------------------------------------------------ | -------- |
| `tokenIn`        | Address of the single token to deposit                             | Yes      |
| `tokenOut`       | Address of the token to receive (usually a receipt or share token) | No       |
| `amountIn`       | Amount of `tokenIn` to deposit in wei (with full decimals)         | Yes      |
| `primaryAddress` | Address of the protocol contract to interact with                  | Yes      |
| `receiver`       | Address to receive the output tokens if not the caller             | No       |

### `multiDeposit`

Deposits multiple tokens into a protocol (like Curve or Balancer) in a single operation.

```ts theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "curve",
      action: "multideposit",
      args: {
        tokenIn: [
          "0x6B175474E89094C44Da98b954EedeAC495271d0F", // DAI
          "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
          "0xdAC17F958D2ee523a2206206994597C13D831ec7", // USDT
        ],
        tokenOut: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", // 3Crv LP token
        amountIn: [
          "10000000000000000000", // 10 DAI (18 decimals)
          "10000000", // 10 USDC (6 decimals)
          "10000000", // 10 USDT (6 decimals)
        ],
        primaryAddress: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", // Curve 3pool
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver
      },
    },
  ],
);
console.log(JSON.stringify(bundle, null, 2));
```

| Parameter        | Description                                                                                       | Required |
| ---------------- | ------------------------------------------------------------------------------------------------- | -------- |
| `tokenIn`        | Array of addresses of tokens to deposit                                                           | Yes      |
| `tokenOut`       | Address of the LP token or receipt token to receive                                               | No       |
| `amountIn`       | Array of amounts to deposit in wei (with full decimals). Must match the length of `tokenIn` array | Yes      |
| `primaryAddress` | Address of the pool or protocol contract to interact with                                         | Yes      |
| `receiver`       | Address to receive the LP tokens if not the caller                                                | No       |

### `tokenizedSingleDeposit`

<Warning>
  **Deprecation Notice:** This action is deprecated in favor of using
  `singleDeposit` with the appropriate protocol. It will be removed in a future
  API version.
</Warning>

Deposits a single token and receives a tokenized position.

```json theme={null}
{
  "protocol": "compound-v3",
  "action": "tokenizedsingledeposit",
  "args": {
    "tokenIn": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
    "tokenOut": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", // cUSDCv3
    "amountIn": "10000000", // 10 USDC (6 decimals)
    "primaryAddress": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", // Compound market
    "receiver": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" // Optional: Receiver
  }
}
```

| Parameter        | Description                                                | Required |
| ---------------- | ---------------------------------------------------------- | -------- |
| `tokenIn`        | Address of token to deposit                                | Yes      |
| `tokenOut`       | Address of tokenized position to receive                   | Yes      |
| `amountIn`       | Amount of `tokenIn` to deposit in wei (with full decimals) | Yes      |
| `primaryAddress` | Address of the protocol contract to interact with          | Yes      |
| `receiver`       | Address to receive the position tokens if not the caller   | No       |

### `tokenizedMultiDeposit`

<Warning>
  **Deprecation Notice:** This action is deprecated in favor of using
  `multiDeposit` with the appropriate protocol. It will be removed in a future
  API version.
</Warning>

Deposits multiple tokens and receives a tokenized position.

```json theme={null}
{
  "protocol": "balancer-v2",
  "action": "tokenizedmultideposit",
  "args": {
    "tokenIn": [
      "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", // WBTC
      "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" // WETH
    ],
    "tokenOut": "0x8353157092ED8Be69a9DF8F95af097bbF33Cb2aF", // BPT token
    "amountIn": [
      "100000", // 0.01 WBTC (8 decimals)
      "10000000000000000" // 0.01 WETH (18 decimals)
    ],
    "primaryAddress": "0x8353157092ED8Be69a9DF8F95af097bbF33Cb2aF", // Balancer Pool
    "receiver": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e" // Optional: Receiver
  }
}
```

| Parameter        | Description                                                                                       | Required |
| ---------------- | ------------------------------------------------------------------------------------------------- | -------- |
| `tokenIn`        | Array of addresses of tokens to deposit                                                           | Yes      |
| `tokenOut`       | Address of the tokenized position to receive                                                      | Yes      |
| `amountIn`       | Array of amounts to deposit in wei (with full decimals). Must match the length of `tokenIn` array | Yes      |
| `primaryAddress` | Address of the protocol contract to interact with                                                 | Yes      |
| `receiver`       | Address to receive the position token if not the caller                                           | No       |

### `multiOutSingleDeposit`

Deposits a single token and receives multiple output tokens (typically used for certain AMM positions).

```ts theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "uniswap-v3",
      action: "multioutsingledeposit",
      args: {
        tokenIn: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
        tokenOut: [
          "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", // WBTC
          "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        ],
        amountIn: "1000000000000000000", // 1 WETH (18 decimals)
        primaryAddress: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640", // Uniswap pool
        receiver: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e", // Optional: Receiver
      },
    },
  ],
);
console.log(JSON.stringify(bundle, null, 2));
```

| Parameter        | Description                                                | Required |
| ---------------- | ---------------------------------------------------------- | -------- |
| `tokenIn`        | Address of the single token to deposit                     | Yes      |
| `tokenOut`       | Array of addresses of tokens to receive                    | Yes      |
| `amountIn`       | Amount of `tokenIn` to deposit in wei (with full decimals) | Yes      |
| `primaryAddress` | Address of the protocol contract to interact with          | Yes      |
| `receiver`       | Address to receive the output tokens if not the caller     | No       |

## Lending Actions

### `flashloan`

Borrows tokens from a lending protocol without upfront collateral, executes callback actions atomically, and repays the debt within the same transaction.

<Info>
  Flashloans have dedicated documentation. See the [Flashloans
  Reference](/pages/build/reference/flashloans) for supported protocols, chain
  availability, parameters, and examples.
</Info>

### `borrow`

Borrows a token from a lending protocol using a deposited token as collateral.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "aave-v3",
      action: "deposit",
      args: {
        tokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address
        tokenOut: "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8", // aWETH address (optional)
        amountIn: "1000000000000000000", // Amount in wei (1 WETH)
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", // Aave V3 pool
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", // Optional: Receiver address
      },
    },
    {
      protocol: "aave-v3",
      action: "borrow",
      args: {
        collateral: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address (collateral)
        tokenOut: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC address (to borrow)
        amountOut: "1000000000", // Amount to borrow in wei (1000 USDC with 6 decimals)
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", // Aave V3 pool
      },
    },
  ],
);
```

| Parameter        | Description                                                                             | Required |
| ---------------- | --------------------------------------------------------------------------------------- | -------- |
| `collateral`     | Address of the token used as collateral. Can be an array for multiple collateral tokens | Yes      |
| `tokenOut`       | Address of the token to borrow                                                          | Yes      |
| `amountOut`      | Amount to borrow in wei (with full decimals)                                            | Yes      |
| `primaryAddress` | Address of the lending pool contract                                                    | Yes      |

### `repay`

Repays a loan on a lending protocol.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "aave-v3",
      action: "deposit",
      args: {
        tokenIn: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address
        tokenOut: "0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8", // aWETH address (optional)
        amountIn: "1000000000000000000", // Amount in wei (1 WETH)
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", // Aave V3 pool
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", // Optional: Receiver address
      },
    },
    {
      protocol: "aave-v3",
      action: "borrow",
      args: {
        collateral: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address (collateral)
        tokenOut: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC address (to borrow)
        amountOut: "1000000000", // Amount to borrow in wei (1000 USDC with 6 decimals)
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", // Aave V3 pool
      },
    },
    // Step 3: Now repay the borrowed USDT
    {
      protocol: "aave-v3",
      action: "repay",
      args: {
        tokenIn: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // USDC
        amountIn: "1000000000", // 100 USDC
        primaryAddress: "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
      },
    },
  ],
);
```

| Parameter        | Description                                 | Required |
| ---------------- | ------------------------------------------- | -------- |
| `tokenIn`        | Address of token to repay                   | Yes      |
| `amountIn`       | Amount to repay in wei (with full decimals) | Yes      |
| `primaryAddress` | Address of the lending pool contract        | Yes      |
| `onBehalfOf`     | Address to repay debt for                   | Yes      |

### `repay` on Behalf of Another address

Repay parameter `onBehalfOf` enables you to repay a loan on behalf of a given address:

```js theme={null}

const bundleData = await client.getBundleData(
  {
    chainId: 1, // Mainnet
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045" as Address,
    routingStrategy: "delegate",
  },
  [
    // 3. Repay the ETH debt
    {
      protocol: "compound-v2",
      action: "repay",
      args: {
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", // ETH
        amountIn: "300000000000000000",
        primaryAddress: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", // cETH contract
        onBehalfOf: "0x80fCBfbC698Aca3B3BCB83ceB13301a639A39832",
      },
    },
  ],
);

console.log(bundleData);
```

## Yield Farming Actions

### `harvest`

Harvests rewards from yield-generating positions.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "curve-gauge",
      action: "harvest",
      args: {
        token: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", // Token address (LP token or gauge token)
        primaryAddress: "0x182B723a58739a9c974cFDB385ceaDb237453c28", // Curve gauge address
      },
    },
  ],
);
```

| Parameter        | Description                                                  | Required |
| ---------------- | ------------------------------------------------------------ | -------- |
| `token`          | Address of token to harvest rewards for                      | Yes      |
| `primaryAddress` | Address of the contract to interact with (e.g., gauge, farm) | Yes      |

## Token Management Actions

### `approve`

Approves a spender to use tokens.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "erc20",
      action: "approve",
      args: {
        token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // WETH address
        spender: "0xe592427a0aece92de3edee1f18e0157c05861564", // Spender address (e.g., Uniswap router)
        amount: "1000000000000000000000000", // Amount to approve in wei (1M WETH)
      },
    },
  ],
);
```

| Parameter | Description                                   | Required |
| --------- | --------------------------------------------- | -------- |
| `token`   | Address of the token to approve               | Yes      |
| `spender` | Address of the spender (protocol or router)   | Yes      |
| `amount`  | Amount to approve in wei (with full decimals) | Yes      |

### `transfer`

Transfers tokens to a specified address.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "erc20",
      action: "transfer",
      args: {
        token: "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", // OMG token address
        receiver: "0x80eba3855878739f4710233a8a19d89bdd2ffb8e", // Recipient address
        amount: "1000000000000000000", // Amount to transfer in wei (1 OMG)
      },
    },
  ],
);
```

| Parameter  | Description                                    | Required |
| ---------- | ---------------------------------------------- | -------- |
| `token`    | Address of the token to transfer               | Yes      |
| `receiver` | Address of the recipient                       | Yes      |
| `amount`   | Amount to transfer in wei (with full decimals) | Yes      |

For NFT transfers see [`erc721:transfer`](#erc721-transfer) below.

### `transferFrom`

Transfers tokens from a specified address to another address.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "balance",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
      },
    },
    {
      protocol: "erc20",
      action: "transferfrom",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // OMG token address
        sender: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", // Sender address
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11", // Recipient address
        amount: "1000000000000000000", // Amount to transfer in wei (1 OMG)
      },
    },
  ],
);
```

| Parameter  | Description                                    | Required |
| ---------- | ---------------------------------------------- | -------- |
| `token`    | Address of the token to transfer               | Yes      |
| `sender`   | Address of the sender                          | Yes      |
| `receiver` | Address of the recipient                       | Yes      |
| `amount`   | Amount to transfer in wei (with full decimals) | Yes      |

For NFT transfers see [`erc721:transferFrom`](#erc721-transferfrom) below.

### `erc721:transfer`

ERC-721 doesn't expose a `transfer(to, tokenId)` selector — this is a UX alias that emits a `transferFrom(walletAddress(), receiver, tokenId)` call. Unlike `erc721:transferFrom`, this action's input schema does **not** accept `sender`; the sender is always the executing wallet at runtime.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "erc721",
      action: "transfer",
      args: {
        token: "0x15da4c7db6404b92894d5214FAc92057Fb8a263d", // NFT collection address
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11", // Recipient
        tokenId: "715", // Token id to transfer
      },
    },
  ],
);
```

| Parameter  | Description                       | Required |
| ---------- | --------------------------------- | -------- |
| `token`    | Address of the ERC-721 collection | Yes      |
| `receiver` | Address of the recipient          | Yes      |
| `tokenId`  | The ERC-721 token id to transfer  | Yes      |

### `erc721:approve`

Approves a spender to operate on a single ERC-721 token id. Mirrors `erc20:approve` but with `tokenId` in place of `amount`.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "erc721",
      action: "approve",
      args: {
        token: "0x15da4c7db6404b92894d5214FAc92057Fb8a263d", // NFT collection address
        spender: "0x000000000000000000000000000000000000dEaD", // Spender address
        tokenId: "715", // Token id to approve
      },
    },
  ],
);
```

| Parameter | Description                                | Required |
| --------- | ------------------------------------------ | -------- |
| `token`   | Address of the ERC-721 collection          | Yes      |
| `spender` | Address allowed to operate on the token id | Yes      |
| `tokenId` | The ERC-721 token id to approve            | Yes      |

<Note>
  ERC-721 `approve(spender, tokenId)` requires `msg.sender` to be the owner of
  the token id (or an approved operator). The effective `msg.sender` inside a
  bundle depends on `routingStrategy` — for `router`, that is the shared
  `EnsoShortcuts` contract; for `delegate` / `ensowallet-v2`, the per-user
  wallet. The token id must be owned by whichever address `msg.sender` resolves
  to at execution time.
</Note>

### `erc721:transferFrom`

Transfers an ERC-721 token id between two addresses. Mirrors `erc20:transferfrom` but takes `tokenId` instead of `amount`, and `sender` is optional — when omitted it defaults to the executing wallet.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "erc721",
      action: "transferfrom",
      args: {
        token: "0x15da4c7db6404b92894d5214FAc92057Fb8a263d", // NFT collection address
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11", // Recipient
        tokenId: "715", // Token id to transfer
      },
    },
  ],
);
```

| Parameter  | Description                                                                  | Required |
| ---------- | ---------------------------------------------------------------------------- | -------- |
| `token`    | Address of the ERC-721 collection                                            | Yes      |
| `receiver` | Address of the recipient                                                     | Yes      |
| `tokenId`  | The ERC-721 token id to transfer                                             | Yes      |
| `sender`   | Address holding the token id. Defaults to the executing wallet when omitted. | No       |

### `permitTransferFrom`

Approves and transfers a token that supports permit in a single step using a signature. A permit signature needs to be generated offchain and passed to the API.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "permit2",
      action: "permittransferfrom",
      args: {
        token: "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07", // Token address
        amount: "1000000000000000000", // Amount in wei (1 token)
        sender: "0xb67f3CE46bB9E1a1127796c27f38DbaB9f643ec0", // Sender address
        receiver: "0x35a2839b617F7da6534d636f22945f6Cb6137130", // Receiver address
        nonce: "1", // Nonce to prevent replay attacks
        deadline: "1710150268", // Timestamp deadline for signature validity
        signature: "0x...", // Permit signature
      },
    },
  ],
);
```

| Parameter   | Description                                                                         | Required |
| ----------- | ----------------------------------------------------------------------------------- | -------- |
| `token`     | Address of the token to transfer. Can be an array for multiple tokens               | Yes      |
| `amount`    | Amount to transfer in wei (with full decimals). Can be an array for multiple tokens | Yes      |
| `sender`    | Address of the sender                                                               | Yes      |
| `receiver`  | Address of the recipient                                                            | Yes      |
| `nonce`     | Nonce value to prevent signature replay                                             | Yes      |
| `deadline`  | Timestamp after which the signature is invalid                                      | Yes      |
| `signature` | The EIP-2612 permit signature                                                       | Yes      |

## Utility Actions

### `balance`

Gets the balance of a token for the caller's address.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "balance",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
      },
    },
  ],
);
```

| Parameter | Description                           | Required |
| --------- | ------------------------------------- | -------- |
| `token`   | Address of the token to check balance | Yes      |

<Info>
  The balance action supports advanced parameters for checking external account
  balances and building routes with estimated amounts. See the [Advanced Balance
  Reference](/pages/build/reference/balance) for details, risk considerations,
  and examples.
</Info>

### `split`

Routes one input amount into more than one `tokenOut`. It returns an array with one amount per output token; downstream actions must select each value with `index`. This is not an arbitrary percentage splitter.

See [Chaining actions](#chaining-actions) for a complete `split -> depositclmm` example. A `split` must feed a compatible later deposit flow; it is not a terminal transfer action.

| Parameter  | Description                                    | Required |
| ---------- | ---------------------------------------------- | -------- |
| `tokenIn`  | Address of the token to split                  | Yes      |
| `tokenOut` | Array of token addresses for the split outputs | Yes      |
| `amountIn` | Base-unit amount or previous output reference  | Yes      |
| `receiver` | Address to receive the split tokens            | No       |

### `merge`

Routes more than one input token into a single output amount.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "merge",
      args: {
        tokenIn: [
          "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
          "0x6b175474e89094c44da98b954eedeac495271d0f", // DAI
        ],
        tokenOut: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // Combined WETH
        amountIn: [
          "2000000", // 2 USDC
          "3000000000000000000", // 3 DAI
        ],
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      },
    },
  ],
);
```

| Parameter  | Description                                         | Required |
| ---------- | --------------------------------------------------- | -------- |
| `tokenIn`  | Array of input token addresses                      | Yes      |
| `tokenOut` | Address of the output token                         | Yes      |
| `amountIn` | Base-unit amount for each corresponding input token | Yes      |
| `receiver` | Address to receive the merged tokens                | No       |

### `call`

Makes an arbitrary contract call for interactions not covered by a public action. The return shape follows the supplied ABI, and output references can be used inside nested argument objects.

<Warning>
  Arbitrary calls can transfer assets or grant permissions. Verify the target, ABI, calldata, and returned transaction before signing. See the [custom contract call guide](/pages/build/examples/custom-call-bundle).
</Warning>

For arithmetic over prior outputs, prefer the documented [Math Actions](#math-actions).

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "call",
      args: {
        address: "0xD0aF6F692bFa10d6a535A3A321Dc8377F4EeEF12", // Contract address
        method: "percentMul", // Method name
        abi: "function percentMul(uint256,uint256) external", // ABI signature
        args: [
          "1000000000000000000", // 1 ETH (first argument)
          "7000", // 70% (second argument)
        ],
      },
    },
  ],
);
```

| Parameter  | Description                                                 | Required |
| ---------- | ----------------------------------------------------------- | -------- |
| `address`  | Address of the contract to call                             | Yes      |
| `method`   | Name of the method to call                                  | Yes      |
| `abi`      | ABI signature of the method                                 | Yes      |
| `args`     | Array of arguments to pass to the method                    | Yes      |
| `tokenIn`  | Input token address (when tokens are involved in the call)  | No       |
| `tokenOut` | Output token address (when tokens are involved in the call) | No       |
| `value`    | ETH value to send with the transaction in wei               | No       |

### `fee`

Calculates and deducts a fee from a specified amount, sending the fee to a designated receiver. This action is typically prepended to bridge operations to facilitate fee collection.

The `fee` action calculates the fee as `amount * (bps/10000)` and sends this amount to the `receiver`. It returns `amount - fee`, which can be used in subsequent actions via `useOutputOfCallAt`.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "fee",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amount: "1000000000000",
        bps: "500",
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      },
    },
  ],
);
```

| Parameter  | Description                                                                              | Required |
| ---------- | ---------------------------------------------------------------------------------------- | -------- |
| `token`    | Token address to apply the fee to                                                        | Yes      |
| `amount`   | Amount to apply the fee to (with full decimals) or a return value from a previous action | Yes      |
| `bps`      | Fee percentage in basis points (1 bps = 0.01%, 100 bps = 1%)                             | Yes      |
| `receiver` | Address to receive the fee                                                               | Yes      |

### `ensofee`

Calculates and deducts Enso's protocol fee from a specified amount.

The `ensofee` action calculates the fee as `amount * (bps/10000)` and sends this amount to Enso.
It returns `amount - fee`, which can be used in subsequent actions via `useOutputOfCallAt`.

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "ensofee",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
        amount: "1000000000", // 1000 USDC
        bps: "25", // 0.25% fee (25 basis points)
      },
    },
  ],
);
```

| Parameter | Description                                                                              | Required |
| --------- | ---------------------------------------------------------------------------------------- | -------- |
| `token`   | Token address to apply the fee to                                                        | Yes      |
| `amount`  | Amount to apply the fee to (with full decimals) or a return value from a previous action | Yes      |
| `bps`     | Fee percentage in basis points (1 bps = 0.01%, 100 bps = 1%)                             | Yes      |

## Math Actions

Math actions perform on-chain arithmetic operations over values from previous actions. These are useful for computing amounts dynamically within a bundle — for example, dividing a routed amount to distribute across multiple recipients.

All math actions use protocol `math` and take two operands: `amountA` and `amountB`. Each operand can be a static value or a reference to a previous action's output via `useOutputOfCallAt`.

### `add`

Returns `amountA + amountB`.

### `sub`

Returns `amountA - amountB`.

### `mul`

Returns `amountA * amountB`.

### `div`

Returns `amountA / amountB` (integer division).

### `min`

Returns the smaller of `amountA` and `amountB`.

### `max`

Returns the larger of `amountA` and `amountB`.

**Example: Route then divide output by 2 and transfer halves to two addresses**

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
    receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
  },
  [
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amountIn: "1000000000000000000",
        slippage: "300",
      },
    },
    {
      protocol: "math",
      action: "div",
      args: {
        amountA: { useOutputOfCallAt: 0 },
        amountB: "2",
      },
    },
    {
      protocol: "erc20",
      action: "transfer",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amount: { useOutputOfCallAt: 1 },
        receiver: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      },
    },
    {
      protocol: "erc20",
      action: "transfer",
      args: {
        token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amount: { useOutputOfCallAt: 1 },
        receiver: "0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11",
      },
    },
  ],
);
```

| Parameter | Description                                                                          | Required |
| --------- | ------------------------------------------------------------------------------------ | -------- |
| `amountA` | First operand — a static uint256 value or a reference to a previous action's output  | Yes      |
| `amountB` | Second operand — a static uint256 value or a reference to a previous action's output | Yes      |

## Helpers Actions

Helpers actions provide on-chain comparison and conditional logic, enabling bundles to branch based on runtime values. These are useful for building guard conditions, assertions, and conditional flows.

All helpers actions use protocol `helpers`.

### Comparison Actions

Compare two uint256 values and return a boolean result. The result can be referenced by subsequent actions.

#### `isequal`

Returns `true` if `amountA == amountB`.

#### `islessthan`

Returns `true` if `amountA < amountB`.

#### `isequalorlessthan`

Returns `true` if `amountA <= amountB`.

#### `isgreaterthan`

Returns `true` if `amountA > amountB`.

#### `isequalorgreaterthan`

Returns `true` if `amountA >= amountB`.

| Parameter | Description                                                                          | Required |
| --------- | ------------------------------------------------------------------------------------ | -------- |
| `amountA` | First operand — a static uint256 value or a reference to a previous action's output  | Yes      |
| `amountB` | Second operand — a static uint256 value or a reference to a previous action's output | Yes      |

### `check`

Asserts that a boolean condition is `true`. If the condition is `false`, the entire transaction reverts. Use this to enforce invariants — for example, ensuring an output amount meets a minimum threshold.

| Parameter   | Description                                                    | Required |
| ----------- | -------------------------------------------------------------- | -------- |
| `condition` | A boolean value or a reference to a comparison action's output | Yes      |

### `not`

Negates a boolean condition. Returns `true` if the input is `false`, and vice versa.

| Parameter   | Description                                                    | Required |
| ----------- | -------------------------------------------------------------- | -------- |
| `condition` | A boolean value or a reference to a comparison action's output | Yes      |

### `toggle`

Selects between two values based on a condition. Returns `amountA` if the condition is `true`, otherwise returns `amountB`. This is the on-chain equivalent of a ternary expression (`condition ? amountA : amountB`).

| Parameter   | Description                                                    | Required |
| ----------- | -------------------------------------------------------------- | -------- |
| `condition` | A boolean value or a reference to a comparison action's output | Yes      |
| `amountA`   | The value to return if the condition is `true`                 | Yes      |
| `amountB`   | The value to return if the condition is `false`                | Yes      |

**Example: Check that a routed amount is above a minimum before proceeding**

```typescript theme={null}
const bundle = await client.getBundleData(
  {
    chainId: 1,
    fromAddress: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
    routingStrategy: "delegate",
  },
  [
    {
      protocol: "enso",
      action: "route",
      args: {
        tokenIn: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
        tokenOut: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        amountIn: "1000000000000000000",
        slippage: "300",
      },
    },
    {
      protocol: "helpers",
      action: "isequalorgreaterthan",
      args: {
        amountA: { useOutputOfCallAt: 0 },
        amountB: "1000000000", // minimum 1000 USDC
      },
    },
    {
      protocol: "helpers",
      action: "check",
      args: {
        condition: { useOutputOfCallAt: 1 },
      },
    },
  ],
);
```

## Protocol Support

The Bundle API supports a wide range of protocols. Some common ones include:

* `aave-v2`, `aave-v3`: Aave lending/borrowing
* `balancer-v2`: Balancer liquidity pools
* `curve`: Curve Finance stable swaps and gauges
* `curve-gauge`: Curve gauges for staking
* `erc20`: Standard token operations
* `erc721`: Non-fungible token operations (`approve`, `transfer`, `transferfrom`)
* `erc4626`: ERC4626 vaults
* `enso`: General Enso operations including route, split, merge, balance, and arbitrary calls
* `math`: On-chain arithmetic operations (`add`, `sub`, `mul`, `div`, `min`, `max`)
* `helpers`: On-chain comparisons and conditional logic (`isequal`, `islessthan`, `isgreaterthan`, `check`, `not`, `toggle`)
* `permit2`: Uniswap's Permit2 operations
* `uniswap-v2`, `uniswap-v3`: Uniswap pools and routers
* `yearn`: Yearn Finance vaults
* `compound-v2`, `compound-v3`: Compound lending/borrowing
* `stargate`: Cross-chain bridging

For a complete list of supported protocols, use the Projects API endpoint: [GET `/standards`](/api-reference/integration/standards).

<div className="text-right text-xs gray-200 font-semibold w-full" style={{marginTop: '0'}}>
  <p style={{
        color: "#b2b2b2"  
    }}>Updated {date_0}</p>
</div>
