Skip to content
/ evmole Public

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

License

Notifications You must be signed in to change notification settings

cdump/evmole

Repository files navigation

EVMole

try it online npm Crates.io PyPI Go

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

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

Go

API documentation

$ go get github.com/cdump/evmole/go
package 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

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

Benchmark

function selectors

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

Dataset evmole rs · js · py · go whatsabi sevm evmhound heimdall
largest1k
1000
addresses

24427
functions
FP addrs 1 🥈 0 🥇 0 🥇 75 18
FN addrs 0 🥇 0 🥇 0 🥇 40 103
FP funcs 192 🥈 0 🥇 0 🥇 720 600
FN funcs 0 🥇 0 🥇 0 🥇 191 114
Time 18ms · 0.3s · 21ms · 0.1s 2.3s 30s(*) 56ms 371s(*)
random50k
50000
addresses

1171102
functions
FP addrs 1 🥇 43 1 693 3
FN addrs 9 🥇 11 10 2903 4669
FP funcs 3 🥇 51 3 10798 29
FN funcs 10 🥇 12 11 3538 4943
Time 0.3s · 2.6s · 0.5s · 5.7s 30s 440s(*) 1.6s 8684s(*)
vyper
780
addresses

21244
functions
FP addrs 0 🥇 30 0 19 0
FN addrs 0 🥇 780 0 300 780
FP funcs 0 🥇 30 0 19 0
FN funcs 0 🥇 21244 0 8273 21244
Time 10ms · 0.2s · 10ms · 85ms 2.0s 34s(*) 26ms 28s(*)

function arguments

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

Dataset evmole rs · js · py · go heimdall
largest1k
24427
functions
Errors 14.1% 🥇
3447
31.1%
7603
Time 0.5s · 1.1s · 0.5s · 2.1s 370s(*)
random50k
1171102
functions
Errors 4.8% 🥇
56464
19.4%
227077
Time 13s · 26s · 15s · 44s 8579s(*)
vyper
21244
functions
Errors 48.4% 🥇
10289
100.0%
21244
Time 0.5s · 1.3s · 0.5s · 1.4s 29s(*)

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 · go whatsabi sevm heimdall
largest1k
24427
functions
Errors 0.0% 🥇
0
68.1%
16623
2.1%
501
25.7%
6268
Errors strict 18.6% 🥇
4549
79.4%
19393
59.0%
14417
54.8%
13386
Time 8.0s · 8.2s · 8.5s · 18s 2.5s 27s(*) 371s(*)
random50k
1160861
functions
Errors 0.0% 🥇
44
30.2%
351060
0.3%
3370
11.5%
133471
Errors strict 6.8% 🥇
78359
58.2%
675111
55.7%
646831
27.6%
320264
Time 157s · 160s · 171s · 354s 51s 2261s(*) 8334s(*)
vyper
21166
functions
Errors 0.5% 🥇
110
100.0%
21166
76.3%
16150
100.0%
21166
Errors strict 4.0% 🥇
850
100.0%
21166
90.2%
19092
100.0%
21166
Time 8.4s · 7.6s · 8.4s · 17s 1.8s 35s(*) 29s(*)

Control Flow Graph

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.8%
640383
63.0%
430011
41.4%
282599
31.9%
217924
21.7%
148166
False Negatives 3.0% 🥇
20482
6.2%
42058
37.0%
252430
58.6%
399842
68.1%
464517
78.3%
534275
Time 19s 643s 49s 28s 206s 158s

dataset largest1k, 1000 contracts, 682,441 blocks

notes

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

versions: evmole v0.8.2; whatsabi v0.25.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

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

License

Stars

Watchers

Forks