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.
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:
// ❌ 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:
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:
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:
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
import { greaterThanOrEqualTo } from "@biconomy/abstractjs";
runtimeERC20BalanceOf({
tokenAddress: USDC,
targetAddress: orchestrator,
constraints: [greaterThanOrEqualTo(parseUnits("90", 6))] // Wait for 90+ USDC
})
Non-Zero Check
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:
// 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:
- Bridge instruction executes
- Swap instruction simulated → fails (no tokens yet)
- MEE waits and retries
- Bridge completes, tokens arrive
- Constraint satisfied → swap executes
Example: Swap and Deposit All
// 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
Use for Unknown Values
Swap outputs, bridge results, slippage
Add Constraints
Protect against unexpected states
Sweep Remaining
Transfer leftover tokens back to user
Set Slippage Tolerance
Use greaterThanOrEqualTo with minimum
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.