Extracts function selectors and arguments from EVM bytecode
npm install evmole




EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including function selectors, arguments, state mutability, and storage layout, 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.
sh
$ npm i evmole
`
`javascript
import { contractInfo } from 'evmole'const code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
console.log( contractInfo(code, {selectors:true, arguments:true, stateMutability:true}) )
// {
// functions:
// {
// selector: '2125b65b',
// bytecodeOffset: 52,
// arguments: 'uint32,address,uint224',
// stateMutability: 'pure'
// },
// ...
`$3
Documentation is available on [docs.rs
`rust
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,
// arguments: Some([Uint(32), Address, Uint(224)]),
// state_mutability: Some(Pure)
// },
// ...
`$3
API documentation
`sh
$ pip install evmole --upgrade
`
`python
from evmole import contract_infocode = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
print( contract_info(code, selectors=True, arguments=True, state_mutability=True) )
Contract(
functions=
Function(
selector=2125b65b,
bytecode_offset=52,
arguments=uint32,address,uint224,
state_mutability=pure),
...
`$3
[API documentation
`sh
$ go get github.com/cdump/evmole/go
`
`go
package mainimport (
"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
}
`$3
Foundry's cast uses the Rust implementation of EVMole
`sh$ 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)
...
`Benchmark
$3
FP/FN - False Positive/False Negative errors; smaller is better
Dataset
evmole rs · js · py
whatsabi
sevm
evmhound
heimdall
smpl
largest1k
1000
addresses
24427
functions
FP addrs
1 🥈
0 🥇
0 🥇
75
18
95
FN addrs
0 🥇
0 🥇
0 🥇
40
103
9
FP funcs
192 🥈
0 🥇
0 🥇
720
600
749
FN funcs
0 🥇
0 🥇
0 🥇
191
114
12
Time
19ms · 0.3s · 25ms
2.3s
37s(*)
63ms
371s(*)
1ms
random50k
50000
addresses
1171102
functions
FP addrs
1 🥇
43
1
693
3
4136
FN addrs
9 🥇
11
10
2903
4669
77
FP funcs
3 🥇
51
3
10798
29
14652
FN funcs
10 🥇
12
11
3538
4943
96
Time
0.5s · 4.7s · 0.8s
46s
2304s(*)
1.9s
8684s(*)
50ms
vyper
780
addresses
21244
functions
FP addrs
0 🥇
30
0
19
0
185
FN addrs
0 🥇
780
0
300
780
480
FP funcs
0 🥇
30
0
19
0
197
FN funcs
0 🥇
21244
0
8273
21244
12971
Time
9ms · 0.1s · 13ms
1.6s
42s(*)
32ms
28s(*)
780µs
$3
Errors - when at least 1 argument is incorrect: (uint256,string) ≠ (uint256,bytes)
Dataset
evmole rs · js · py
heimdall
smpl
largest1k
24427
functions
Errors
14.0% 🥇
3410
31.1%
7603
58.3%
14242
Time
0.6s · 2.0s · 0.6s
370s(*)
1ms
random50k
1171102
functions
Errors
4.5% 🥇
52670
19.4%
227077
54.9%
643213
Time
17s · 55s · 19s
8579s(*)
50ms
vyper
21244
functions
Errors
48.5% 🥇
10299
100.0%
21244
56.8%
12077
Time
0.4s · 1.6s · 0.5s
29s(*)
780µs
$3
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 | whatsabi | sevm | heimdall | smpl | |
| largest1k 24427 functions | Errors | 0.0% 🥇 0 | 68.1% 16623 | 2.1% 501 | 25.7% 6268 | 2.6% 643 |
| Errors strict | 18.6% 🥇 4555 | 79.4% 19393 | 59.0% 14417 | 54.8% 13386 | 60.9% 14864 | |
| Time | 9.5s · 14s · 9.4s | 3.0s | 41s(*) | 371s(*) | 1ms | |
| random50k 1160861 functions | Errors | 0.0% 🥇 44 | 30.2% 351060 | 0.3% 3370 | 11.5% 133471 | 2.2% 24961 |
| Errors strict | 6.8% 🥇 78923 | 58.2% 675111 | 55.7% 646831 | 27.6% 320264 | 57.7% 670318 | |
| Time | 183s · 276s · 187s | 79s | 2176s(*) | 8334s(*) | 50ms | |
| vyper 21166 functions | Errors | 0.5% 🥇 110 | 100.0% 21166 | 76.3% 16150 | 100.0% 21166 | 1.8% 390 |
| Errors strict | 4.0% 🥇 854 | 100.0% 21166 | 90.2% 19092 | 100.0% 21166 | 59.6% 12610 | |
| Time | 9.8s · 12s · 9.6s | 1.7s | 38s(*) | 29s(*) | 780µs | |
False Negatives - Valid blocks possibly incorrectly marked unreachable by CFG analysis. Lower count usually indicates better precision.
| evmole | ethersolve | evm-cfg | sevm | heimdall-rs | evm-cfg-builder | |
| Basic Blocks | 97.0% 🥇 661959 | 93.7% 639175 | 63.0% 430011 | 41.4% 282599 | 31.9% 217924 | 21.7% 148166 |
| False Negatives | 3.0% 🥇 20482 | 6.3% 43266 | 37.0% 252430 | 58.6% 399842 | 68.1% 464517 | 78.3% 534275 |
| Time | 34s | 1202s | 40s | 42s | 206s | 308s |
dataset largest1k, 1000 contracts, 682,441 blocks
See benchmark/README.md for the methodology and commands to reproduce these results
versions: evmole v0.7.2; whatsabi v0.19.0; sevm v0.7.4; evm-hound-rs v0.1.4; heimdall-rs v0.8.6
(*): 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.