# WhatsABI: guessing an ABI from bytecode, and the proxies hiding behind it

> WhatsABI recovers an ABI-like interface from EVM bytecode when the source is not verified, resolves the proxies that sit in front of a contract, and looks the selectors up in public signature databases. It is written in TypeScript with one runtime dependency so that it can run inside a wallet or a block explorer page.

**shazow/whatsabi** — Extract the ABI (and resolve proxies, and get other metadata) from Ethereum bytecode, even without source code.

- Repository: https://github.com/shazow/whatsabi
- Website: https://shazow.github.io/whatsabi/
- Stars: 1,162 · Forks: 94
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/shazow-whatsabi

## What it does, and who is already using it

WhatsABI guesses an ABI and detects proxies from Ethereum bytecode, even if the contract is unverified. The README calls it perfect for building procedural frontends, embedding in wallets, block explorers, or doing bytecode analysis, and the list of projects using it is a good description of the use case.

The feature list is short enough to state completely. It returns selectors from bytecode. It looks up function signatures from selectors. It has helpers for looking up ABI and signatures from public databases, naming Sourcify, Etherscan, Blockscout, OpenChain and 4Byte. It resolves proxy contracts. And it is a small bundle, less than 15 KB, that works with Ethers.js, Viem and others.

The adopters explain why the library exists. Otterscan uses it for contract interactions in a block explorer, Sourcify uses it for proxy resolving in a verified source code API, Rivet is a developer wallet and devtools for Anvil, Ondora is a cross-chain explorer and search engine, and there is a thirdweb SDK with automatic ABI resolution and a transaction builder. In each case the pattern is the same: a piece of software that has an address and needs to know what the contract at that address can do, without a source file and without asking the user to paste an ABI.

That is the procedural frontend idea in a sentence. A block explorer that cannot show you the decoded arguments of a transaction is a worse explorer, and the address is all the user usually has.

The project is MIT licensed, the primary language is TypeScript, the default branch is `main`, the last push was on 2026-09-27, and the recent releases are v0.29.0 from 2026-08-28, v0.28.0 from 2026-08-18 and v0.27.0 from 2026-07-10. The author is Andrey Petrov.

## The three claims that make it different

The README has a section explaining how WhatsABI differs from other EVM analysis tools, and the three points are the design brief.

The first is that it is built in TypeScript with minimal dependencies, so that it is runnable in the browser and embeddable in wallets. That constraint is the reason there is exactly one runtime dependency, and it is what makes the library usable in a page that must not pull in a node runtime.

The second is that the algorithms used are limited to `O(instructions)` with a small constant factor, so that complex contracts do not cause it to time out or use unbounded memory. A bytecode analysis that walks the code once, in linear time, can be run on any address the user pastes, including a deliberately obfuscated one, without the page hanging. That bound is the difference between a library you can expose to a stranger and one you cannot.

The third is that it does not rely on source code, so it works with unverified contracts, and it does not assume the source language, so it can work for source languages other than Solidity, Vyper or even hand-written assembly. The second half of that point is less obvious and more valuable, because the alternative approach of looking for a source file in a repository assumes both that the contract is verified and that it was written in Solidity.

The last line of the section is about the licence. WhatsABI is permissively open source, MIT licensed, so that anyone can use it, and the README notes the practical consequence: a block explorer or a wallet can take the dependency without a legal review of its own.

## Selectors first, then a database for the names

The mechanics are best understood as two steps, and the README breaks the helper down into them.

The first step is to load the bytecode and extract the callable selectors:

```typescript
const code = await provider.getCode(address);

const selectors = whatsabi.selectorsFromBytecode(code);
console.log(selectors);
// -> ["0x06fdde03", "0x46423aa7", "0x55944a42", ...]
```

The second step turns those selectors into an ABI-like list of interfaces, with a type, a hash or a selector, and the flags that were recovered:

```typescript
const abi = whatsabi.abiFromBytecode(code);
console.log(abi);
// -> [
//  {"type": "event", "hash": "0x721c20121297512b72821b97f5326877ea8ecf4bb9948fea5bfcb6453074d37f"},
//  {"type": "function", "payable": true, "selector": "0x06fdde03", ...},
```

The names come from the databases, and that is a separate layer. The loaders cover Sourcify, Etherscan V2, Blockscout, OpenChain and 4Byte, and they can be combined:

```typescript
const loader = new whatsabi.loaders.MultiABILoader([
  new whatsabi.loaders.SourcifyABILoader(),
  new whatsabi.loaders.EtherscanV2ABILoader({
    apiKey: "...",
  }),
  new whatsabi.loaders.BlockscoutABILoader({
    apiKey: "...",
  }),
]);
```

The call that follows it, `loader.getContract(address)`, returns the ABI, the name and the other metadata.

The MultiABILoader tries each source in turn until a result is found, which is a sensible default ordering, since a verified source is better than a name guessed from a selector collision. The event side has its own loaders, and the same OpenChain signature lookup resolves a function selector to a name such as `name()` and an event hash to a signature such as `CounterIncremented(uint256,address)`.

The thing to keep in mind is the confidence gradient. A selector extracted from bytecode is a fact. A function name attached to it by a database lookup is a guess, and the library is honest about the difference by keeping the two in different structures.

## Proxies are the reason a contract address is not a contract

The proxy resolution is the feature that a block explorer cannot do without, and it works in two modes.

In the first mode, the proxies are reported and the caller decides. The result object carries a `proxies` field, and `followProxies()` resolves them:

```typescript
if (result.followProxies) {
    console.log("Proxies detected:", result.proxies);

    result = await result.followProxies();
    console.log(result.abi);
}
```

In the second mode, the resolution is done for you, and the result is destructured to the resolved address:

```typescript
const { abi, address } = await whatsabi.autoload(
    "0x4f8AD938eBA0CD19155a835f617317a6E788c868",
    {
        provider,
        followProxies: true,
    },
);

console.log("Resolved to:", address);
```

The reason this matters is the one from the earlier section. A proxy contract holds almost no code of its own, so an ABI recovered from the bytecode of a proxy is the ABI of the implementation, and the address in the URL is not the address whose code you are looking at. A tool that shows the functions of the address the user pasted, when that address is a proxy, shows the wrong thing.

There is an `addressResolver` option in the autoload options, a function that maps a name to an address, which is the hook for a deployment registry or a naming service.

The autoload helper also reports what it was able to find, and the README is explicit that the detail varies depending on whether the source code for the address was available, whether bytecode-loaded selector signatures were available, or whether WhatsABI had to guess everything from just the bytecode. A caller can tell the difference, which is the difference between rendering a contract and guessing at it.

## One helper, with hooks for progress and failure

The do-it-all entry point is `autoload`, and it is where the options live.

```typescript
let result = await whatsabi.autoload(address, {
  provider: provider,

  ... whatsabi.loaders.defaultsWithEnv({
    CHAIN_ID: 42161,
    ETHERSCAN_API_KEY: "MYSECRETAPIKEY",
  }),

  onProgress: (phase) => console.log("autoload progress", phase),
  onError: (phase, context) => console.log("autoload error", phase, context),
});
```

Three things are worth noting in that snippet. The `defaultsWithEnv` helper assembles the default loaders with configuration taken from the environment, which is how a chain id and an API key get into the default Sourcify, Etherscan and Blockscout loaders without every call site repeating them. The `onProgress` and `onError` hooks take a phase name, so a UI can show which step of the multi-source lookup is running and which one failed, which matters when a lookup is a sequence of network requests to several services. And the `addressResolver` override is the hook for resolving a name to an address.

The remaining options are switches rather than functions. `followProxies` decides whether proxies are resolved. `enableExperimentalMetadata` turns on the metadata that is not yet stable. The commented-out `abiLoader` and `signatureLoader` lines show that either half of the pipeline can be replaced independently, which is the escape hatch for a project that wants to use its own indexer or its own signature database.

The `autoload` function also works with any provider, and the README shows it with Ethers.js and with Viem, the second example using a Viem public client passed as the provider. The library does not care which client library is in use, because all it needs is the bytecode.

## The engineering around the library

The repository is organised like a library that has to be embedded somewhere, and the build shows it.

The package is `@shazow/whatsabi` at version 0.29.0, it is an ES module package, and it is built three times: `build:esm` and `build:cjs` with TypeScript, each copying a package manifest into the output directory, and `build:types` for the declarations, with `build:docs` running typedoc. The published files are the three output directories plus the sources, the examples and the configuration files, and the exports map points at the type declarations and the two module builds.

The size claim is enforced rather than asserted. There is a `size-limit` configuration in the repository and the `publish` target in the Makefile runs `size-limit` after packing, so a change that pushes the bundle past its budget fails the release rather than being noticed afterwards.

The test setup is the part that is most interesting, because the library is meant to work with several client libraries. The `test-providers` target runs the suite five times, once for each provider configuration: ethers, web3, viem, viem with a transport and viem with a public client. The same library has to behave identically through five different clients, and the way to keep that honest is to run the tests against all five.

The Makefile also has the small chores that a project with generated code accumulates. There is a `generated` target that produces `src/_generated-interfaces.ts` from `examples/index-interfaces.ts`, with a comment that it has to be done in two steps because of a circular import, and a `run-examples` target that runs the example scripts against an address given on the command line. The examples directory holds `autoload.ts`, `bytecode.ts`, `dot.ts`, `resolveproxy.ts`, `index-interfaces.ts` and a benchmark for selectors.

The development environment is reproducible in a slightly unusual way. There is a `flake.nix` and a `flake.lock` at the root, and the example environment file says to save it as `.env` and fill in the keys, and that it gets automatically sourced by `nix develop`. The keys are the usual ones, an Infura key, an Etherscan key and a Blockscout key, plus a `PROVIDER` variable that selects between ethers and viem.

## Where WhatsABI is the wrong tool

The limits follow from what the library can actually know, and the most important one is a limit of the data rather than of the code.

A four byte selector does not identify a function. It identifies a set of candidate functions, and a signature database narrows that set by popularity and by convention, and the result is a guess that is usually right and occasionally wrong. The library is careful to keep the selector and the name in separate structures, and a caller who wants certainty has to treat the recovered name as a rendering hint. An application that builds a form from the recovered ABI, and lets a user sign a transaction with it, is trusting a lookup with no verification behind it.

The second is that the recovery is bounded by what the bytecode contains. A function that is never called, that is constructed dynamically, or that lives behind a delegatecall pattern the analysis does not follow, will not appear in the recovered interface. So the recovered ABI is a lower bound on the contract's real surface, and a frontend that hides the missing entries is hiding functionality from the user.

The third is the alternative, and for a contract you control it is obvious. If you own the contract, the ABI is an artefact of your build, and reading it from the repository is exact. WhatsABI is for the case where you do not own the contract, which is the whole point, and a team that reaches for it to analyse its own code has picked the expensive tool for the easy problem.

There is a middle path worth naming, and it is the one Sourcify already provides: use a verified source when the contract is verified, and fall back to a guess when it is not. The difference in approach is that WhatsABI is a guess engine with loaders attached, and a verification-first design asks the source first and treats the guess as the exception. For a block explorer, where most contracts are unverified, the guess is the common case rather than the exception, and that is the case the library is built for.

## Conclusion

WhatsABI fits a team building a procedural interface over contracts it does not control, a wallet that has to show something meaningful for an unverified address, or a block explorer that wants to render a transaction it cannot verify. It is the wrong tool for a contract you own and can compile, where the real ABI is a build artefact, and it is the wrong tool if the application acts on the recovered function names, because a name looked up in a signature database is a guess about a four byte selector and nothing more. Before depending on it, check whether the contract is verified and use the source when it is, treat the guessed ABI as a rendering hint rather than an interface, and read the proxy resolution before you let a user sign anything against the address you resolved to.

## FAQ

### How do I get an ABI from an unverified Ethereum contract?

Call `whatsabi.autoload(address, { provider })`, which returns a result whose abi is recovered from the bytecode, with function and event selectors and guessed signatures. It works with any provider or client library, such as Ethers.js, Viem or Web3.js, and the lower level `selectorsFromBytecode` and `abiFromBytecode` do the extraction on their own.

### Does WhatsABI detect and resolve proxy contracts?

Yes. The result carries a proxies field, `followProxies()` resolves them, and autoload can follow them directly with the followProxies option, which returns the resolved address alongside the ABI. There is also an addressResolver option for mapping a name to an address.

### Which signature and ABI sources does WhatsABI query?

Loaders exist for Sourcify, Etherscan V2, Blockscout, OpenChain and 4Byte, and a MultiABILoader tries each source in turn until a result is found. The `defaultsWithEnv` helper assembles the default loaders with configuration such as a chain id and an Etherscan API key taken from the environment.

### Can WhatsABI run inside a browser or a wallet?

Yes, and that is a stated design constraint: it is built in TypeScript with minimal dependencies so that it is runnable in the browser and embeddable in wallets, with a bundle of less than 15 KB that works with Ethers.js, Viem and others.

## Sources

- [License: MIT](https://github.com/shazow/whatsabi/blob/main/LICENSE)
- [Project website](https://shazow.github.io/whatsabi/)
- [README](https://github.com/shazow/whatsabi/blob/main/README.md)
- [Releases](https://github.com/shazow/whatsabi/releases)
- [shazow/whatsabi on GitHub](https://github.com/shazow/whatsabi)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/shazow-whatsabi
