Skip to content

Repository files navigation

EVMole

try it onlinenpmCrates.ioPyPIGo

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.

Key Features

  • 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.

Usage

JavaScript

API documentation and usage examples (Node.js, Vite, webpack, Parcel, esbuild)

npm i evmole
import{contractInfo}from'evmole'constcode='0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'console.log(contractInfo(code,{selectors:true,arguments:true,stateMutability:true}))// {// functions: [// {// selector: '2125b65b',// bytecodeOffset: 52,// dispatch: 'abi',// arguments: 'uint32,address,uint224',// stateMutability: 'pure'// },// ...

Rust

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)// },// ...

Python

API documentation

pip install evmole --upgrade
fromevmoleimportcontract_infocode='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

API documentation

go get github.com/cdump/evmole/go
package main
import (
"context""encoding/hex""fmt""github.com/cdump/evmole/go"
)
funcmain() {
code, _:=hex.DecodeString("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256")
info, _:=evmole.ContractInfo(context.Background(), code, evmole.Options{
Selectors: true,
Arguments: true,
StateMutability: true,
})
for_, fn:=rangeinfo.Functions {
fmt.Printf("%s: %s @ %d\n", fn.Selector, *fn.Arguments, fn.BytecodeOffset)
}
// 2125b65b: uint32,address,uint224 @ 52// b69ef8a8: @ 68
}

Foundry

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)
...

AI agents

For application code, use one of the language bindings above. For agent-driven bytecode analysis, choose one integration.

JSON CLI

Use for one-off analysis and scripts:

npx -y evmole analyze --bytecode 0x...

Portable skill

Install routing and interpretation guidance for supported agents:

npx skills add cdump/evmole --skill evm-bytecode-analysis -g

Local MCP server

Expose EVMole as a typed local tool:

npx -y evmole-mcp

All 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.

Benchmark

function selectors

FP/FN - False Positive/False Negative errors; smaller is better

Datasetevmolers · js · py · gowhatsabisevmevmhoundheimdall
coverage2k
solidity

2000
addresses

45650
functions
FP addrs0 🥇41563
FN addrs0 🥇110 🥇224166
FP funcs0 🥇101061627
FN funcs0 🥇1450 🥇821342
Time24ms · 0.3s · 31ms · 0.1s2.0s28s(*)86ms111s(*)
random10k
solidity

10000
addresses

223316
functions
FP addrs0 🥇19162247
FN addrs0 🥇441838819
FP funcs0 🥇651123157260
FN funcs0 🥇173741121021
Time0.1s · 0.8s · 0.2s · 0.8s7.1s80s(*)0.4s533s(*)
coverage1k
vyper

1000
addresses

38759
functions
FP addrs0 🥇5600 🥇20 🥇
FN addrs0 🥇998788525998
FP funcs0 🥇5600 🥇50 🥇
FN funcs0 🥇38759340771621838759
Time91ms · 0.4s · 0.1s · 0.3s1.9s5.4s(*)67ms12s(*)

function arguments

Errors - when at least 1 inferred argument is incorrect: (uint256,string)(uint256,bytes)

Datasetevmolers · js · py · goheimdall
coverage2k
solidity

45650
functions
Errors8.5% 🥇
3883
23.3%
10643
Time0.8s · 1.7s · 1.1s · 2.1s111s(*)
random10k
solidity

223316
functions
Errors6.8% 🥇
15296
21.4%
47878
Time3.5s · 6.9s · 4.2s · 9.7s511s(*)
coverage1k
vyper

38759
functions
Errors45.4% 🥇
17590
100.0%
38759
Time0.5s · 1.1s · 0.6s · 1.4s12s(*)

function state mutability

Errors - Results are not equal (treating view and pure as equivalent to nonpayable)

Errors strict - Results are strictly unequal (nonpayableview). Some ABIs mark pure/view functions as nonpayable, so not all strict errors indicate real issues.

Datasetevmolers · js · py · gowhatsabisevmheimdall
coverage2k
solidity

45647
functions
Errors0.0% 🥇
18
52.2%
23810
11.2%
5133
19.6%
8951
Errors strict9.0% 🥇
4107
70.9%
32346
62.3%
28429
41.3%
18835
Time10s · 11s · 9.7s · 20s3.1s29s(*)112s(*)
random10k
solidity

223273
functions
Errors0.0% 🥇
39
48.8%
108928
9.3%
20713
18.6%
41507
Errors strict8.9% 🥇
19940
69.9%
156081
60.9%
136069
40.1%
89519
Time48s · 50s · 46s · 88s12s81s(*)512s(*)
coverage1k
vyper

38278
functions
Errors0.1% 🥇
34
100.0%
38278
96.2%
36814
100.0%
38278
Errors strict1.2% 🥇
441
100.0%
38278
98.4%
37650
100.0%
38278
Time45s · 39s · 44s · 89s2.0s5.5s(*)12s(*)

Control Flow Graph

False Negatives - Valid blocks possibly incorrectly marked unreachable by CFG analysis. Lower count usually indicates better precision.

evmolers · js · py · goethersolveevm-cfgsevmheimdall-rsevm-cfg-builder
Basic Blocks92.8% 🥇
483212
52.5%
273518
58.6%
305248
37.3%
194368
32.6%
169980
14.5%
75383
False Negatives7.2% 🥇
37496
47.5%
247190
41.4%
215460
62.7%
326340
67.4%
350728
85.5%
445325
Time14s · 26s · 12s · 55s888s36s9.8s20s359s

dataset flow-challenge500, 500 contracts, 520,708 blocks

notes

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

How it works

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.

Talks

License

MIT

About

Extracts function selectors, arguments, state mutability and storage layout from EVM bytecode, even for unverified contracts

Topics

Resources

Stars

458 stars

Watchers

4 watching

Forks

Releases

Used by

Contributors

Languages