abi-typegen
abi-typegen is a Rust CLI. It reads compiled Solidity artifacts and writes typed
bindings for 14 targets: six TypeScript SDKs, Python, Go, Rust, Swift, C#, Kotlin,
Solidity interfaces, and YAML. It reads Foundry and Hardhat layouts, and it can
generate several targets in one run.
Why abi-typegen
When you compile a contract, the compiler writes a JSON file called the ABI that lists its functions, events, and errors. abi-typegen turns that file into typed code, so your editor and compiler catch a wrong argument before it reaches the chain.
- One native binary. No Node or Python runtime is needed to generate.
- Reads Foundry
out/and Hardhatartifacts/contracts/. generate --checkfails CI when committed bindings are stale.diffshows what would change.jsonprints the parsed ABI.fetchdownloads a verified ABI from any Etherscan-compatible explorer.- A Hardhat plugin regenerates on every compile. For Foundry,
abi-typegen forge-installprints a shell function that addsforge typegen.
Install
Install the CLI with cargo, download a prebuilt binary, or add the npm package to a JavaScript project.
Rust CLI
Prebuilt binaries are on GitHub Releases.
Hardhat plugin
To add only the packaged binary to a Node project:
Quick start
After your contracts compile, one command writes the typed files into your project.
Foundry
Build the contracts, then generate:
Minimal foundry.toml:
[]
= "src/generated"
= "viem" # or "viem,python" or ["viem", "python"]
watch regenerates whenever the artifacts change:
The zod target emits Zod 4 code, so install zod@4 in the consuming project.
Hardhat
import { defineConfig } from "hardhat/config";
import abiTypegen from "@0xdoublesharp/hardhat-abi-typegen";
export default defineConfig({
plugins: [abiTypegen],
solidity: "0.8.34",
typegen: {
out: "src/generated",
target: "viem",
contracts: ["Token"],
exclude: ["*Test"],
},
});
The plugin generates bindings on every compile:
Hardhat 2 configs can still import "@0xdoublesharp/hardhat-abi-typegen", which
loads a CommonJS fallback. The fallback is also available directly at
@0xdoublesharp/hardhat-abi-typegen/hardhat2.
Multi-target generation
Set several targets in the config file or on the command line:
# foundry.toml
[]
= ["viem", "python", "rust"] # also accepts "viem,python,rust"
Each target gets its own subdirectory under the output path:
src/generated/
viem/
python/
rust/
Fetch and generate from a block explorer
You can generate bindings for a contract you didn't compile, as long as its
source is verified on an Etherscan-compatible explorer. fetch downloads the
ABI, saves it as a local artifact, and generates bindings in one command:
With the default viem target, that writes:
out/WETH.sol/WETH.json ← saved artifact
src/generated/WETH.abi.ts
src/generated/WETH.viem.ts
src/generated/index.ts
From a local ABI file
--file skips the network request. It accepts a bare ABI array ([...]) or a
Foundry/Hardhat artifact ({"abi": [...]}):
API key
Most explorers need an API key. Put it in .env in the working directory or
export it:
# .env
ETHERSCAN_API_KEY=your_key_here
You can also pass it per command:
Supported networks
--network has shortcuts for more than 80 networks, including these:
| Group | Names |
|---|---|
| Ethereum | mainnet, sepolia, holesky, hoodi |
| OP Stack | optimism, base, blast, fraxtal, worldchain, unichain |
| Arbitrum | arbitrum, arbitrum-nova, arbitrum-sepolia |
| Polygon | polygon, polygon-amoy |
| BNB Chain | bsc, opbnb |
| Avalanche | avalanche, fuji |
| Other L2s | linea, scroll, zksync, mantle, sonic, taiko |
| Alt L1s | gnosis, moonbeam, moonriver, celo, fantom, cronos, berachain, sei |
| Newer chains | hyperevm, abstract, monad, megaeth, apechain, katana |
For an explorer that isn't listed, pass its API URL with --url:
The tool uses the Etherscan V2 unified endpoint, so every chain on docs.etherscan.io/supported-chains works.
Targets
Each target writes code for a specific library, so pick the one your app already uses.
| Target | Flag | Language | Primary ecosystem |
|---|---|---|---|
| viem | --target viem |
TypeScript | viem contract helpers |
| zod | --target zod |
TypeScript | Zod 4 validation schemas |
| wagmi | --target wagmi |
TypeScript | wagmi v2 and v3 React hooks |
| ethers v6 | --target ethers |
TypeScript | ethers v6 |
| ethers v5 | --target ethers5 |
TypeScript | ethers v5 |
| web3.js | --target web3js |
TypeScript | web3.js v4 |
| Python | --target python |
Python | web3.py |
| Go | --target go |
Go | go-ethereum |
| Rust | --target rust |
Rust | alloy |
| Swift | --target swift |
Swift | web3swift |
| C# | --target csharp |
C# | Nethereum |
| Kotlin | --target kotlin |
Kotlin | web3j |
| Solidity interfaces | --target solidity |
Solidity | External contract interfaces |
| YAML | --target yaml |
YAML | Human-readable ABI descriptions |
Configuration
Settings live in foundry.toml or hardhat.config.ts, so every run uses the
same options. Command-line flags override them for a single run.
Foundry (foundry.toml)
[]
= "src/generated" # output directory
= "viem" # string, "a,b,c", or ["a", "b", "c"]
= true # emit typed wrapper files when supported
= [] # [] = all; or ["MyToken", "Vault"]
= [] # glob patterns: ["*Test", "*Mock", "I*"]
Hardhat (hardhat.config.ts)
typegen: {
out: "src/generated",
target: "viem", // string, "a,b,c", or ["a", "b", "c"]
wrappers: true,
contracts: [],
exclude: [],
}
CLI overrides
Commands
generate writes the bindings. The other commands check, preview, watch, or
fetch.
# fetch ABI from a block explorer and generate bindings
# import a local ABI file and generate bindings
What gets generated
Each contract produces one or two files. TypeScript targets get the ABI plus a typed wrapper, and other languages get a single file:
- TypeScript wrapper targets emit
<Name>.abi.tsand a wrapper such as<Name>.viem.tsor<Name>.ethers.ts. zodemits<Name>.abi.tsand<Name>.zod.ts, written against Zod 4 (import * as z from 'zod').solidityemitsI<Name>.solwith the interface, events, custom errors, and structs rebuilt from ABI tuples.- Other targets emit one file per contract (
.py,.go,.rs,.swift,.cs,.kt, or.yaml). - Multi-target runs write each target to its own directory.
Overloaded functions get a suffix built from their parameter types:
deposit(uint256)->depositUint256deposit(uint256,address)->depositUint256Address
Wrappers also type transaction options. Payable functions accept a value in
every TypeScript wrapper: ethers overrides, the wagmi write options, web3
send, and viem's own write options.
Example output
viem:
export function getTokenContract<TClient extends Client>(
address: Address,
client: TClient,
): GetContractReturnType<typeof TokenAbi, TClient> {
return getContract({ address, abi: TokenAbi, client });
}
export type TokenTransferParams = {
to: `0x${string}`;
amount: bigint;
};
Rust:
docs/generated-output.md covers every target in more detail.
Performance
Generation speed depends on your machine and contracts, so the repo includes a
benchmark you can run yourself. ./e2e/bench.sh 10 compares the sample
generation workflows.
Results depend on tool versions, targets, contracts, and whether you count
build and task overhead. Comparison and benchmark guidance
explains the method and its limits.
CI
A CI check catches a contract change that was committed without regenerated bindings.
Foundry
- run: forge build
- run: abi-typegen generate --check
Hardhat
- run: npx hardhat compile
- run: git diff --exit-code src/generated/
Docs
This README covers the basics, and docs/ has the details. Start with the
documentation index:
- Installation and download verification
- Configuration and generated output
- Forge integration
- Development and optional build/cache storage
- Release process and changelog
Contributing
Bug reports and fixes are welcome. CONTRIBUTING.md covers setup, code conventions, tests, and pull requests. Bug fixes need a regression test.
Disposable build storage
This section is optional and only matters if you work on abi-typegen itself.
Builds and package installs use their normal local paths by default, and nothing
requires a Scratch volume or compiler cache. To send build output and caches to
another directory, run python3 .cargo/setup-scratch.py --root PATH once. Cargo,
pnpm, and Make then read that saved setting. python3 .cargo/setup-scratch.py --disable
turns it off. The storage guide covers requirements,
moving the location, and keeping existing data.