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

# Composable Batching

> Use dynamic values that resolve at execution time

Runtime injection lets you use values that aren't known until execution—like token balances after a swap or bridge.

## The Problem

Traditional batching requires hardcoded values:

```typescript theme={null}
// ❌ This breaks if swap output differs from expected
const depositAmount = parseUnits("0.05", 18);  // Guessing WETH output
```

## The Solution

Use runtime functions to inject actual values at execution time:

```typescript theme={null}
import { runtimeERC20BalanceOf, greaterThanOrEqualTo } from "@biconomy/abstractjs";
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";

// ✅ Uses actual balance when executing
const depositAmount = runtimeERC20BalanceOf({
  tokenAddress: WETH,
  targetAddress: account.addressOn(base.id, true),
  constraints: [balanceNotZeroConstraint]
});
```

## Available Functions

| Function                          | Description                        |
| --------------------------------- | ---------------------------------- |
| `runtimeERC20BalanceOf`           | ERC-20 token balance               |
| `runtimeNativeBalanceOf`          | Native token balance (ETH, MATIC)  |
| `runtimeERC20AllowanceOf`         | Token allowance between addresses  |
| `runtimeParamViaCustomStaticCall` | Any contract read (up to 32 bytes) |

## runtimeERC20BalanceOf

Most common—inject token balance at execution:

```typescript theme={null}
import { runtimeERC20BalanceOf } from "@biconomy/abstractjs";

const transferAll = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: USDC,
    abi: erc20Abi,
    functionName: "transfer",
    args: [
      recipient,
      runtimeERC20BalanceOf({
        tokenAddress: USDC,
        targetAddress: account.addressOn(base.id, true),
        constraints: []
      })
    ]
  }
});
```

## runtimeNativeBalanceOf

For native token operations:

```typescript theme={null}
import { runtimeNativeBalanceOf } from "@biconomy/abstractjs";

const sendAllETH = await account.buildComposable({
  type: "nativeTokenTransfer",
  data: {
    chainId: base.id,
    to: recipient,
    value: runtimeNativeBalanceOf({
      targetAddress: account.addressOn(base.id, true)
    })
  }
});
```

## Constraints

Constraints control **when** instructions execute and protect against bad values.

### Minimum Balance

```typescript theme={null}
import { greaterThanOrEqualTo } from "@biconomy/abstractjs";

runtimeERC20BalanceOf({
  tokenAddress: USDC,
  targetAddress: orchestrator,
  constraints: [greaterThanOrEqualTo(parseUnits("90", 6))]  // Wait for 90+ USDC
})
```

### Non-Zero Check

```typescript theme={null}
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";

runtimeERC20BalanceOf({
  tokenAddress: WETH,
  targetAddress: orchestrator,
  constraints: [balanceNotZeroConstraint]  // Wait for any WETH
})
```

## Transaction Ordering

Constraints determine execution sequence. MEE retries until satisfied:

```typescript theme={null}
// Step 1: Bridge (executes immediately)
const bridge = await account.buildComposable({ /* bridge config */ });

// Step 2: Swap (waits for bridge tokens)
const swap = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    args: [
      runtimeERC20BalanceOf({
        tokenAddress: USDC_BASE,
        targetAddress: account.addressOn(base.id, true),
        constraints: [greaterThanOrEqualTo(minExpected)]  // ← Waits here
      })
    ]
  }
});
```

**Flow:**

1. Bridge instruction executes
2. Swap instruction simulated → fails (no tokens yet)
3. MEE waits and retries
4. Bridge completes, tokens arrive
5. Constraint satisfied → swap executes

## Example: Swap and Deposit All

```typescript theme={null}
// Swap USDC → WETH
const swap = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: UNISWAP_ROUTER,
    abi: UniswapAbi,
    functionName: "exactInputSingle",
    args: [{ tokenIn: USDC, tokenOut: WETH, amountIn: parseUnits("100", 6), /* ... */ }]
  }
});

// Approve exactly what we received
const approve = await account.buildComposable({
  type: "approve",
  data: {
    chainId: base.id,
    spender: MORPHO_POOL,
    tokenAddress: WETH,
    amount: runtimeERC20BalanceOf({
      tokenAddress: WETH,
      targetAddress: account.addressOn(base.id, true),
      constraints: [balanceNotZeroConstraint]
    })
  }
});

// Deposit all WETH
const deposit = await account.buildComposable({
  type: "default",
  data: {
    chainId: base.id,
    to: MORPHO_POOL,
    abi: MorphoAbi,
    functionName: "deposit",
    args: [
      runtimeERC20BalanceOf({
        tokenAddress: WETH,
        targetAddress: account.addressOn(base.id, true),
        constraints: [balanceNotZeroConstraint]
      }),
      account.addressOn(base.id, true)
    ]
  }
});

const quote = await meeClient.getQuote({
  instructions: [swap, approve, deposit],
  feeToken: { address: USDC, chainId: base.id }
});
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use for Unknown Values" icon="check">
    Swap outputs, bridge results, slippage
  </Card>

  <Card title="Add Constraints" icon="shield">
    Protect against unexpected states
  </Card>

  <Card title="Sweep Remaining" icon="broom">
    Transfer leftover tokens back to user
  </Card>

  <Card title="Set Slippage Tolerance" icon="sliders">
    Use `greaterThanOrEqualTo` with minimum
  </Card>
</CardGroup>

## Summary

Runtime injection turns static transactions into **adaptive, state-aware flows** that handle:

* Unknown swap outputs
* Bridge timing
* Slippage protection
* Automatic sequencing

No more guessing values or stuck transactions.
