Skip to content

evs

Write typed EVM read scripts in plain TypeScript. Compile them to bytecode. Run dozens of on-chain reads in a single eth_call — no contracts to deploy, full viem inference end to end.
import { evscript, arg, t, compile } from '@maxencerb/evs';
import { erc20Abi } from 'viem';
const tokenMeta = evscript({ name: 'tokenMeta', args: [arg('token', t.address)] }, (s) => {
const { token } = s.args;
const symbol = s.call({ abi: erc20Abi, address: token, functionName: 'symbol' });
const decimals = s.call({ abi: erc20Abi, address: token, functionName: 'decimals' });
return s.return({ symbol, decimals });
});
const script = compile(tokenMeta);
// → runtime bytecode + a literal-typed ABI viem can infer from

One round trip

Sequential, dependent reads — pool → tokens → metadata — collapse into a single eth_call. No multicall contracts, no waterfall of requests.

Typed end to end

Calls are typed like viem’s readContract. The compiled artifact carries an as const ABI, so readContract infers your script’s argument and return types exactly.

Nothing to deploy

Scripts execute deploylessly via the eth_call code parameter or a state override. The chain never sees a deployment; any standard RPC node works.

Solidity-grade semantics

Checked arithmetic with solc 0.8 panic codes, verbatim revert bubbling, and a differential test suite that pins the bytecode against a reference interpreter, viem’s codecs, and real solc output.