EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including function selectors, arguments, state mutability, persistent and transient storage layouts, and CBOR metadata, even for unverified contracts.
- Multi-language support: Available as JavaScript, Rust, Python, and Go libraries.
- High accuracy and performance: Outperforms existing tools.
- Broad compatibility: Tested with both Solidity and Vyper compiled contracts.
- Lightweight: Clean codebase with minimal external dependencies.
- Unverified contract analysis: Extracts information even from unverified bytecode.
- Selector dispatch classification: Distinguishes normal ABI dispatch from selectors handled by fallback logic.
- CBOR metadata: Extracts string-keyed values from a terminal, length-suffixed CBOR map without assuming a particular compiler.
API documentation and usage examples (Node.js, Vite, webpack, Parcel, esbuild)
npm i evmoleimport { contractInfo } from 'evmole'
const code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
console.log( contractInfo(code, {selectors:true, arguments:true, stateMutability:true}) )
// {
// functions: [
// {
// selector: '2125b65b',
// bytecodeOffset: 52,
// dispatch: 'abi',
// arguments: 'uint32,address,uint224',
// stateMutability: 'pure'
// },
// ...Documentation is available on docs.rs
let code = hex::decode("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256").unwrap();
println!("{:?}", evmole::contract_info(
evmole::ContractInfoArgs::new(&code)
.with_selectors()
.with_arguments()
.with_state_mutability()
)
);
// Contract {
// functions: Some([
// Function {
// selector: [33, 37, 182, 91],
// bytecode_offset: 52,
// dispatch: Abi,
// arguments: Some([Uint(32), Address, Uint(224)]),
// state_mutability: Some(Pure)
// },
// ...pip install evmole --upgradefrom evmole import contract_info
code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
print( contract_info(code, selectors=True, arguments=True, state_mutability=True) )
# Contract(
# functions=[
# Function(
# selector=2125b65b,
# bytecode_offset=52,
# dispatch="abi",
# arguments=uint32,address,uint224,
# state_mutability=pure),
# ...go get github.com/cdump/evmole/gopackage main
import (
"context"
"encoding/hex"
"fmt"
"github.com/cdump/evmole/go"
)
func main() {
code, _ := hex.DecodeString("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256")
info, _ := evmole.ContractInfo(context.Background(), code, evmole.Options{
Selectors: true,
Arguments: true,
StateMutability: true,
})
for _, fn := range info.Functions {
fmt.Printf("%s: %s @ %d\n", fn.Selector, *fn.Arguments, fn.BytecodeOffset)
}
// 2125b65b: uint32,address,uint224 @ 52
// b69ef8a8: @ 68
}Foundry's cast uses the Rust implementation of EVMole
$ cast selectors $(cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)
0x06fdde03 view
0x095ea7b3 address,uint256 nonpayable
0x18160ddd view
0x23b872dd address,address,uint256 nonpayable
...
$ cast selectors --resolve $(cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)
0x06fdde03 view name()
0x095ea7b3 address,uint256 nonpayable approve(address,uint256)
0x18160ddd view totalSupply()
0x23b872dd address,address,uint256 nonpayable transferFrom(address,address,uint256)
...For application code, use one of the language bindings above. For agent-driven bytecode analysis, choose one integration.
Use for one-off analysis and scripts:
npx -y evmole analyze --bytecode 0x...Install routing and interpretation guidance for supported agents:
npx skills add cdump/evmole --skill evm-bytecode-analysis -gExpose EVMole as a typed local tool:
npx -y evmole-mcpAll integrations expect deployed/runtime bytecode and run locally without sending bytecode to an EVMole-operated service. See the agent integration guide for setup, schemas, limitations, and privacy details.
FP/FN - False Positive/False Negative errors; smaller is better
| Dataset | evmole rs · js · py · go | whatsabi | sevm | evmhound | heimdall | |
| coverage2k solidity 2000 addresses 45650 functions |
FP addrs | 0 🥇 | 4 | 1 | 56 | 3 |
| FN addrs | 0 🥇 | 11 | 0 🥇 | 224 | 166 | |
| FP funcs | 0 🥇 | 10 | 10 | 616 | 27 | |
| FN funcs | 0 🥇 | 145 | 0 🥇 | 821 | 342 | |
| Time | 24ms · 0.3s · 31ms · 0.1s | 2.0s | 28s(*) | 86ms | 111s(*) | |
| random10k solidity 10000 addresses 223316 functions |
FP addrs | 0 🥇 | 19 | 16 | 224 | 7 |
| FN addrs | 0 🥇 | 44 | 1 | 838 | 819 | |
| FP funcs | 0 🥇 | 65 | 112 | 3157 | 260 | |
| FN funcs | 0 🥇 | 173 | 7 | 4112 | 1021 | |
| Time | 0.1s · 0.8s · 0.2s · 0.8s | 7.1s | 80s(*) | 0.4s | 533s(*) | |
| coverage1k vyper 1000 addresses 38759 functions |
FP addrs | 0 🥇 | 560 | 0 🥇 | 2 | 0 🥇 |
| FN addrs | 0 🥇 | 998 | 788 | 525 | 998 | |
| FP funcs | 0 🥇 | 560 | 0 🥇 | 5 | 0 🥇 | |
| FN funcs | 0 🥇 | 38759 | 34077 | 16218 | 38759 | |
| Time | 91ms · 0.4s · 0.1s · 0.3s | 1.9s | 5.4s(*) | 67ms | 12s(*) | |
Errors - when at least 1 inferred argument is incorrect: (uint256,string) ≠ (uint256,bytes)
| Dataset | evmole rs · js · py · go | heimdall | |
| coverage2k solidity 45650 functions |
Errors | 8.5% 🥇 3883 |
23.3% 10643 |
| Time | 0.8s · 1.7s · 1.1s · 2.1s | 111s(*) | |
| random10k solidity 223316 functions |
Errors | 6.8% 🥇 15296 |
21.4% 47878 |
| Time | 3.5s · 6.9s · 4.2s · 9.7s | 511s(*) | |
| coverage1k vyper 38759 functions |
Errors | 45.4% 🥇 17590 |
100.0% 38759 |
| Time | 0.5s · 1.1s · 0.6s · 1.4s | 12s(*) | |
Errors - Results are not equal (treating view and pure as equivalent to nonpayable)
Errors strict - Results are strictly unequal (nonpayable ≠ view). Some ABIs mark pure/view functions as nonpayable, so not all strict errors indicate real issues.
| Dataset | evmole rs · js · py · go | whatsabi | sevm | heimdall | |
| coverage2k solidity 45647 functions |
Errors | 0.0% 🥇 18 |
52.2% 23810 |
11.2% 5133 |
19.6% 8951 |
| Errors strict | 9.0% 🥇 4107 |
70.9% 32346 |
62.3% 28429 |
41.3% 18835 |
|
| Time | 10s · 11s · 9.7s · 20s | 3.1s | 29s(*) | 112s(*) | |
| random10k solidity 223273 functions |
Errors | 0.0% 🥇 39 |
48.8% 108928 |
9.3% 20713 |
18.6% 41507 |
| Errors strict | 8.9% 🥇 19940 |
69.9% 156081 |
60.9% 136069 |
40.1% 89519 |
|
| Time | 48s · 50s · 46s · 88s | 12s | 81s(*) | 512s(*) | |
| coverage1k vyper 38278 functions |
Errors | 0.1% 🥇 34 |
100.0% 38278 |
96.2% 36814 |
100.0% 38278 |
| Errors strict | 1.2% 🥇 441 |
100.0% 38278 |
98.4% 37650 |
100.0% 38278 |
|
| Time | 45s · 39s · 44s · 89s | 2.0s | 5.5s(*) | 12s(*) | |
False Negatives - Valid blocks possibly incorrectly marked unreachable by CFG analysis. Lower count usually indicates better precision.
| evmole rs · js · py · go | ethersolve | evm-cfg | sevm | heimdall-rs | evm-cfg-builder | |
| Basic Blocks | 92.8% 🥇 483212 |
52.5% 273518 |
58.6% 305248 |
37.3% 194368 |
32.6% 169980 |
14.5% 75383 |
| False Negatives | 7.2% 🥇 37496 |
47.5% 247190 |
41.4% 215460 |
62.7% 326340 |
67.4% 350728 |
85.5% 445325 |
| Time | 14s · 26s · 12s · 55s | 888s | 36s | 9.8s | 20s | 359s |
dataset flow-challenge500, 500 contracts, 520,708 blocks
See benchmark/README.md for the methodology and commands to reproduce these results
versions: evmole v0.9.3; whatsabi v0.25.0; sevm v0.7.4; evm-hound-rs v0.1.4; heimdall-rs v0.9.3
(*): sevm and heimdall-rs are full decompilers, not limited to extracting function selectors
EVMole uses symbolic execution with a custom EVM implementation to trace how CALLDATA flows through the bytecode:
This approach is more accurate than static pattern matching because it follows the actual execution paths the EVM would take, correctly handling complex dispatchers, proxy patterns, and compiler-specific optimizations from both Solidity and Vyper.
- EVMole: function selectors and arguments from bytecode - BlockSplit 2024
- EVMole: function selectors and arguments from bytecode - EthCC 2024
- Reconstructing Control Flow Graphs from EVM Bytecode - ETHTaipei 2025
- Reconstructing Control Flow Graphs from EVM Bytecode: Faster, Better, Stronger - EthCC 2025
MIT