13 unstable releases (3 breaking)

0.6.2 Dec 7, 2024
0.5.1 Sep 26, 2024
0.3.7 Jul 26, 2024
0.3.3 Feb 26, 2024
0.3.1 Dec 24, 2023

#384 in Magic Beans

Download history 1711/week @ 2024-08-25 2740/week @ 2024-09-01 926/week @ 2024-09-08 1388/week @ 2024-09-15 1586/week @ 2024-09-22 1438/week @ 2024-09-29 1589/week @ 2024-10-06 2000/week @ 2024-10-13 1170/week @ 2024-10-20 1180/week @ 2024-10-27 1135/week @ 2024-11-03 1119/week @ 2024-11-10 1310/week @ 2024-11-17 2056/week @ 2024-11-24 1411/week @ 2024-12-01 1435/week @ 2024-12-08

6,292 downloads per month

MIT license

220KB
5K SLoC

EVMole

try it online npm Crates.io PyPI

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.

Key Features

  • Multi-language support: Available as JavaScript, Rust, and Python 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.

Usage

JavaScript

API documentation and usage examples (node, vite, webpack, parcel, esbuild)

$ npm i evmole
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'
//     },
//     ...

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,
//             arguments: Some([Uint(32), Address, Uint(224)]),
//             state_mutability: Some(Pure)
//         },
//         ...

Python

API documentation

$ pip install evmole --upgrade
from 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,
#             arguments=uint32,address,uint224,
#             state_mutability=pure),
#     ...

Foundry

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

Benchmark

function selectors

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 111 9
FP funcs 192 🥈 0 🥇 0 🥇 720 600 749
FN funcs 0 🥇 0 🥇 0 🥇 191 147 12
Time 0.4s · 0.6s · 0.4s 2.8s 35s(*) 0.4s 343s(*) 1.6s
random50k
50000
addresses

1171102
functions
FP addrs 1 🥇 43 1 693 3 4136
FN addrs 9 🥇 11 10 2903 4708 77
FP funcs 3 🥇 51 3 10798 29 14652
FN funcs 10 🥇 12 11 3538 6098 96
Time 4.3s · 9.0s · 7.2s 53s 1124s(*) 5.8s 8572s(*) 44s
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 0.3s · 0.6s · 0.4s 2.0s 46s(*) 0.3s 28s(*) 1.0s

function arguments

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%
7593
58.3%
14242
Time 1.0s · 2.1s · 1.1s 344s(*) 0.5s
random50k
1171102
functions
Errors 4.5% 🥇
52670
19.4%
227612
54.9%
643213
Time 20s · 49s · 24s 8517s(*) 8.1s
vyper
21244
functions
Errors 48.5% 🥇
10299
100.0%
21244
56.8%
12077
Time 0.7s · 1.4s · 0.8s 28s(*) 0.5s

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.

Dataset evmole rs · js · py whatsabi sevm heimdall smpl
largest1k
24427
functions
Errors 0.0% 🥇
0
68.1%
16623
2.1%
501
25.4%
6201
2.6%
643
Errors strict 18.6% 🥇
4555
79.4%
19393
59.0%
14417
54.9%
13403
60.9%
14864
Time 8.9s · 16s · 8.9s 3.8s 33s(*) 339s(*) 0.5s
random50k
1160861
functions
Errors 0.0% 🥇
44
30.2%
351060
0.3%
3370
11.6%
134196
2.2%
24961
Errors strict 6.8% 🥇
78923
58.2%
675111
55.7%
646831
27.7%
321495
57.7%
670318
Time 169s · 334s · 172s 88s 1131s(*) 8267s(*) 8.2s
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.5s · 13s · 9.7s 2.1s 38s(*) 28s(*) 0.5s

See benchmark/README.md for the methodology and commands to reproduce these results

versions: evmole v0.6.2; whatsabi v0.17.0; sevm v0.7.3; evm-hound-rs v0.1.4; heimdall-rs v0.8.4

(*): sevm and heimdall-rs are full decompilers, not limited to extracting function selectors

How it works

Short: Executes code with a custom EVM and traces CALLDATA usage.

Long: TODO

License

MIT

Dependencies

~9.5MB
~176K SLoC