๐ Runtime Parameter Injection
Runtime injection gives developers a powerful way to compose transactions when some values โ like token amounts โ arenโt known until execution time. This is a major evolution beyond traditional static batching, where all parameters must be known upfront.
Instead of asking users to guess or estimate how much will arrive after a bridge or swap, AbstractJS lets you define placeholders โ runtime values โ that get resolved dynamically when the transaction is executed.

๐ก Why It Matters
Traditional transaction batching is rigid:
- You must hardcode every parameter
- You can't adapt to bridges, swaps, or slippage
- You often over- or under-estimate received amounts
With runtime injection, orchestration becomes adaptive:
- Use actual token balances at the moment of execution
- Protect users with constraints (e.g. slippage limits)
- Compose flows across chains with fewer assumptions
This eliminates entire classes of user experience problems and makes your apps more reliable.
๐งโ๐ป Basic Usage
import { runtimeERC20BalanceOf } from "@biconomy/abstractjs";
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";
const swapInstruction = await oNexus.buildComposable({
type: "default",
data: {
chainId: optimism.id,
to: "0xProtocolAddress",
abi: protocolAbi,
functionName: "swap",
args: [
runtimeERC20BalanceOf({
tokenAddress: "0xTokenAddress",
targetAddress: nexusAddress,
constraints: [balanceNotZeroConstraint]
})
]
}
});
๐ ๏ธ Available Runtime Functions
Function | Description |
---|---|
runtimeERC20BalanceOf | Inject the ERC20 balance of a target address at runtime |
runtimeERC20BalanceOf
Inject the token balance of an address โ most often your orchestrator address โ at the time of execution.
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";
const transferAll = await mcNexus.buildComposable({
type: "default",
data: {
to: tokenContract,
abi: erc20Abi,
functionName: "transfer",
args: [
recipient,
runtimeERC20BalanceOf({
tokenAddress: tokenAddress,
targetAddress: mcNexus.addressOn(chain.id),
constraints: [balanceNotZeroConstraint]
})
],
chainId: chain.id
}
});
๐งฑ Constraints = Execution Control
Constraints ensure that runtime-injected values meet specific criteria. If constraints are not met, the instruction will not execute.
But even more importantly โ they determine when instructions will execute. This is key for cross-chain orchestration.
โ๏ธ Transaction Ordering
When a runtime value is used, the orchestration system will wait until all constraints are satisfied before executing. This means constraints enforce dependency:
await oNexus.buildComposable({
chainId: optimism.id,
args: [
runtimeERC20BalanceOf({
tokenAddress: usdcOnOptimism,
targetAddress: nexusAddress,
constraints: [balanceNotZeroConstraint]
})
]
});
This instruction wonโt execute until nexusAddress
has a non-zero balance of USDC.
In multi-chain orchestration, this is the mechanism that controls execution sequencing.
๐ธ Handling Slippage
constraints: [
greaterThanOrEqualTo((expectedAmount * 90n) / 100n)
]
Use constraints to ensure execution only proceeds under acceptable conditions.
๐ก๏ธ Safety Nets
Constraints act like assert statements. If the value isn't good โ donโt execute.
๐ Cross-Chain Example
import { balanceNotZeroConstraint } from "../utils/balanceNotZero.util";
// Step 1: Bridge
const bridgeInstruction = await oNexus.buildComposable({
// bridging config
});
// Step 2: Wait for funds, then swap
const swapInstruction = await oNexus.buildComposable({
type: "default",
data: {
chainId: optimism.id,
to: "0xSwapProtocol",
abi: swapAbi,
functionName: "swapExactTokensForTokens",
args: [
runtimeERC20BalanceOf({
tokenAddress: usdcOnOptimism,
targetAddress: nexusAddress,
constraints: [balanceNotZeroConstraint]
})
]
}
});
- MEE simulates the swap instruction
- Simulation fails (no tokens yet)
- MEE waits and retries
- Bridge completes
- Constraints satisfied โ transaction proceeds
โ Best Practices
- โ Use runtime injection when values are unknown ahead of time
- โ Always apply constraints to protect users and define execution rules
- โ Use with transfer instructions to sweep remaining balances
- โ Design for graceful failure if constraints aren't met
โ๏ธ How It Works
AbstractJS builds orchestration callData with placeholders. During execution:
- The smart account receives
executeComposable()
- The fallback handler reads blockchain state
- Runtime values are injected into calldata
- The final transaction executes with those resolved parameters
This eliminates the need for custom smart contracts or multi-step user flows.
๐ง Summary
Runtime injection turns your orchestration flows into state-aware, auto-sequencing logic that adapts to bridge results, swap outputs, slippage conditions, and more.
Use it when:
- Youโre building cross-chain workflows
- You canโt predict output values
- You want fewer failures and better UX