abi-typegen 0.5.0

Generate bindings from Solidity ABI artifacts (Foundry, Hardhat)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# abi-typegen

<p align="center">
  <img src="docs/abi-typegen.png" alt="abi-typegen" width="360" />
</p>

<p align="center"><strong>Typed bindings from Solidity ABI artifacts.</strong></p>
<p align="center">Point it at Foundry or Hardhat output and get client code for the SDK you use.</p>
<p align="center">
  <a href="https://github.com/doublesharp/abi-typegen/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/doublesharp/abi-typegen/ci.yml?branch=main&label=ci"></a>
  <a href="https://github.com/doublesharp/abi-typegen/actions/workflows/coverage.yml"><img alt="Coverage workflow" src="https://img.shields.io/github/actions/workflow/status/doublesharp/abi-typegen/coverage.yml?branch=main&label=coverage"></a>
  <a href="https://doublesharp.github.io/abi-typegen/coverage/"><img alt="Coverage report" src="https://img.shields.io/badge/coverage-report-2ea043"></a>
  <a href="https://crates.io/crates/abi-typegen"><img alt="crates.io" src="https://img.shields.io/crates/v/abi-typegen"></a>
  <a href="https://www.npmjs.com/package/@0xdoublesharp/abi-typegen"><img alt="npm" src="https://img.shields.io/npm/v/@0xdoublesharp/abi-typegen?label=npm"></a>
</p>

`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 Hardhat `artifacts/contracts/`.
- `generate --check` fails CI when committed bindings are stale.
- `diff` shows what would change. `json` prints the parsed ABI.
- `fetch` downloads a verified ABI from any Etherscan-compatible explorer.
- A Hardhat plugin regenerates on every compile. For Foundry,
  `abi-typegen forge-install` prints a shell function that adds `forge typegen`.

## Install

Install the CLI with cargo, download a prebuilt binary, or add the npm package
to a JavaScript project.

### Rust CLI

```sh
cargo install abi-typegen
```

Prebuilt binaries are on [GitHub Releases](https://github.com/doublesharp/abi-typegen/releases).

### Hardhat plugin

```sh
pnpm add -D @0xdoublesharp/hardhat-abi-typegen
```

To add only the packaged binary to a Node project:

```sh
pnpm add -D @0xdoublesharp/abi-typegen
```

## Quick start

After your contracts compile, one command writes the typed files into your
project.

### Foundry

Build the contracts, then generate:

```sh
forge build
abi-typegen generate
```

Minimal `foundry.toml`:

```toml
[abi-typegen]
out = "src/generated"
target = "viem"            # or "viem,python" or ["viem", "python"]
```

`watch` regenerates whenever the artifacts change:

```sh
abi-typegen watch
```

The `zod` target emits Zod 4 code, so install `zod@4` in the consuming project.

### Hardhat

```ts
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:

```sh
npx hardhat 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:

```toml
# foundry.toml
[abi-typegen]
target = ["viem", "python", "rust"]   # also accepts "viem,python,rust"
```

```sh
abi-typegen generate --target viem,python,rust
```

Each target gets its own subdirectory under the output path:

```text
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:

```sh
abi-typegen fetch --name WETH --network mainnet \
  0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
```

With the default `viem` target, that writes:

```text
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": [...]}`):

```sh
abi-typegen fetch --name WETH --file ./WETH.abi.json
```

### API key

Most explorers need an API key. Put it in `.env` in the working directory or
export it:

```sh
# .env
ETHERSCAN_API_KEY=your_key_here
```

You can also pass it per command:

```sh
abi-typegen fetch --name WETH --network mainnet --api-key $KEY 0xc02aaa...
```

### 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`:

```sh
abi-typegen fetch --name MyToken --url https://api.sonicscan.org/api 0xabc...
```

The tool uses the Etherscan V2 unified endpoint, so every chain on
[docs.etherscan.io/supported-chains](https://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]https://viem.sh/ contract helpers                |
| zod                 | `--target zod`      | TypeScript | [Zod]https://zod.dev/ 4 validation schemas             |
| wagmi               | `--target wagmi`    | TypeScript | [wagmi]https://wagmi.sh/ v2 and v3 React hooks         |
| ethers v6           | `--target ethers`   | TypeScript | [ethers]https://docs.ethers.org/v6/ v6                 |
| ethers v5           | `--target ethers5`  | TypeScript | ethers v5                                                |
| web3.js             | `--target web3js`   | TypeScript | [web3.js]https://docs.web3js.org/ v4                   |
| Python              | `--target python`   | Python     | [web3.py]https://web3py.readthedocs.io/                |
| Go                  | `--target go`       | Go         | [go-ethereum]https://geth.ethereum.org/                |
| Rust                | `--target rust`     | Rust       | [alloy]https://alloy.rs/                               |
| Swift               | `--target swift`    | Swift      | [web3swift]https://github.com/web3swift-team/web3swift |
| C#                  | `--target csharp`   | C#         | [Nethereum]https://nethereum.com/                      |
| Kotlin              | `--target kotlin`   | Kotlin     | [web3j]https://docs.web3j.io/                          |
| 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`)

```toml
[abi-typegen]
out       = "src/generated"          # output directory
target    = "viem"                   # string, "a,b,c", or ["a", "b", "c"]
wrappers  = true                     # emit typed wrapper files when supported
contracts = []                       # [] = all; or ["MyToken", "Vault"]
exclude   = []                       # glob patterns: ["*Test", "*Mock", "I*"]
```

### Hardhat (`hardhat.config.ts`)

```ts
typegen: {
  out: "src/generated",
  target: "viem",              // string, "a,b,c", or ["a", "b", "c"]
  wrappers: true,
  contracts: [],
  exclude: [],
}
```

### CLI overrides

```sh
abi-typegen generate \
  --artifacts ./out \
  --out ./types \
  --target viem \
  --contracts Token,Vault \
  --exclude "*Test,*Mock" \
  --no-wrappers \
  --clean
```

## Commands

`generate` writes the bindings. The other commands check, preview, watch, or
fetch.

```sh
abi-typegen generate             # write generated bindings
abi-typegen generate --hardhat   # use Hardhat artifact layout
abi-typegen generate --check     # fail if output is stale
abi-typegen generate --clean     # remove stale generated files
abi-typegen diff                 # show what would change without writing
abi-typegen json --pretty        # dump parsed ABI summary as JSON
abi-typegen watch                # watch artifacts and regenerate on change
abi-typegen fetch --name <NAME> --network <NETWORK> <ADDRESS>
                                 # fetch ABI from a block explorer and generate bindings
abi-typegen fetch --name <NAME> --file <ABI.json>
                                 # import a local ABI file and generate bindings
abi-typegen init                 # scaffold [abi-typegen] in foundry.toml
abi-typegen forge-install        # install Forge shell integration
```

## 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.ts` and a wrapper such as
  `<Name>.viem.ts` or `<Name>.ethers.ts`.
- `zod` emits `<Name>.abi.ts` and `<Name>.zod.ts`, written against Zod 4
  (`import * as z from 'zod'`).
- `solidity` emits `I<Name>.sol` with 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`). Rust files are snake_case with a generated `mod.rs`.
- Go, Rust, Swift, and Kotlin files embed the ABI, every selector and signature,
  and a named type for each tuple.
- Multi-target runs write each target to its own directory.

Overloaded functions in TypeScript get a suffix built from their parameter types:

- `deposit(uint256)` -> `depositUint256`
- `deposit(uint256,address)` -> `depositUint256Address`

Go, Rust, Swift, and Kotlin number overloads the way their SDKs do (`Deposit`,
`Deposit0` in Go; `deposit_0Call` in alloy).

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:

```ts
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:

```rust
alloy::sol! {
    #[sol(rpc, abi, all_derives, extra_derives(serde::Serialize, serde::Deserialize))]
    contract Token {
        event Transfer(address indexed from, address indexed to, uint256 value);
        function transfer(address to, uint256 amount) external returns (bool);
    }
}
```

[docs/generated-output.md](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](docs/comparison.md)
explains the method and its limits.

## CI

A CI check catches a contract change that was committed without regenerated
bindings.

### Foundry

```yaml
- run: forge build
- run: abi-typegen generate --check
```

### Hardhat

```yaml
- 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](docs/README.md):

- [Installation]docs/installation.md and download verification
- [Configuration]docs/configuration.md and [generated output]docs/generated-output.md
- [Forge integration]docs/forge-integration.md
- [Development]docs/development.md and [optional build/cache storage]docs/development-storage.md
- [Release process]docs/releasing.md and [changelog]CHANGELOG.md

## Contributing

Bug reports and fixes are welcome. [CONTRIBUTING.md](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](docs/development-storage.md) covers requirements,
moving the location, and keeping existing data.