FSM Multicall Executor · EIP-1193 Native
Batched on-chain reads.
domino solves the N×M RPC problem. Define step dependencies directly — step 2 takes step 1's output, and domino resolves the entire task graph in M multicalls instead of N×M individual calls.
defineTasksingle-use graph built
naive sequential loopN×M calls bypassed
multicall #1balanceOf(owner)
multicall #2convertToAssets(ref)
t.derive()local calculation
resolved in 2 multicalls16.8 KB gzipped
Topology
One graph, M multicalls
Multicall is great for batched reads. But when step 2 depends on step 1, naive code makes N×M calls. domino's FSM engine automatically computes graph depth and executes one multicall per step batch.
- fsm execution
- Results from step N flow automatically into step N+1 parameters via typed Ref<T> handles.
- eip-1193 native
- Direct integration with viem PublicClient, window.ethereum, or ethers adapters — zero extra protocol layer.
- multichain parallel
- MultichainResolver executes identical task graphs across multiple EVM chains in parallel.
Procedure
Workflow
Four simple steps to replace N×M RPC loops with self-triggering multicalls.
-
01
npm install @halaprix/domino viemInstall domino and viem. Works with Node.js 18+ and modern browser environments. -
02
const executor = new Eip1193Executor(provider);Wrap any EIP-1193 provider (viem PublicClient, window.ethereum, or an ethers adapter). -
03
const position = defineTask((t) => { ... });Describe call dependencies using human-readable ABIs or standard JSON ABIs. Pass result refs directly to subsequent call args. -
04
const [result] = await runMultistepTasks(executor, [position()]);Execute the task graph. domino runs minimum required multicalls and returns a fully typed result object.
Guarantees
Capabilities & Contracts
domino is engineered for strict production guarantees and zero runtime bloat.
| Guarantee | Metric | Meaning |
|---|---|---|
| gzip transfer budget | 16.8 KB | Strict bundle ceiling enforced in CI — whole library including Multicall3 bytecodes and viem utils. |
| single-use task protection | Guaranteed | Tasks are single-run; re-submitting an existing task instance throws DominoTaskReuseError to prevent race conditions. |
| memoized abi parser | LRU 256 | Deduplicates human-readable ABI parsing across array-identity and value-equality boundaries. |
| per-task isolation | runSettled | Isolate task failures with runSettled / resolver.runSettled without aborting the entire batch. |
| typescript inference | Strict | Return types inferred directly from human-readable ABIs and t.derive callbacks. |
Performance
N×M Calls vs. Domino M Multicalls
By collapsing dependent calls into M sequential multicalls, domino dramatically reduces RPC roundtrips and latency.
- problem Querying 100 vaults with a 2-step pipeline (`balanceOf` → `convertToAssets`) requires 200 sequential HTTP/WebSocket RPC roundtrips.
- solution domino groups step 1 across all 100 vaults into Multicall #1, feeds all 100 balance outputs into Multicall #2, completing the pipeline in 2 multicalls.
- erc4626 resolver Built-in helper `resolveErc4626Vault({ executor, vault, owner })` resolves vault balances, total assets, share prices, and user positions in 2 multicalls.
Install
Setup & Quick Start
Version 1.3.0 is available on npm. Lightweight, fully typed, and zero external runtime configuration.
$ npm install @halaprix/domino viem
# define a multistep task graph with domino
import { createPublicClient, http } from "viem";
import { Eip1193Executor, defineTask, runMultistepTasks } from "@halaprix/domino";
const provider = createPublicClient({ transport: http() });
const executor = new Eip1193Executor(provider);
function vaultPosition(vault, owner) {
return defineTask((t) => {
const balance = t.call({ target: vault, abi: vaultAbi, functionName: "balanceOf", args: [owner] });
const assets = t.call({ target: vault, abi: vaultAbi, functionName: "convertToAssets", args: [balance] });
const hasBalance = t.derive([balance], (bal) => bal > 0n);
return { balance, assets, hasBalance };
});
}
const [res] = await runMultistepTasks(executor, [vaultPosition(vaultAddr, ownerAddr)]);
Supports viem, window.ethereum, ethers v5/v6 (via EIP-1193 adapter), and custom RPC transport wrappers.