Awesome
EVMole
EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including function selectors, arguments, and state mutability, 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 { functionSelectors, functionArguments, functionStateMutability } from 'evmole'
const code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
console.log(functionSelectors(code)); // [ '2125b65b', 'b69ef8a8' ]
console.log(functionArguments(code, '2125b65b')); // 'uint32,address,uint224'
console.log(functionStateMutability(code, '2125b65b')); // 'pure'
Rust
Documentation is available on docs.rs
let code = hex::decode("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256").unwrap();
println!("{:x?} | {} | {:?}",
evmole::function_selectors(&code, 0),
evmole::function_arguments(&code, &[0x21, 0x25, 0xb6, 0x5b], 0),
evmole::function_state_mutability(&code, &[0x21, 0x25, 0xb6, 0x5b], 0),
);
// [[21, 25, b6, 5b], [b6, 9e, f8, a8]] | uint32,address,uint224 | Pure
Python
$ pip install evmole --upgrade
from evmole import function_selectors, function_arguments, function_state_mutability
code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
print(function_selectors(code)) # ['2125b65b', 'b69ef8a8']
print(function_arguments(code, '2125b65b')) # uint32,address,uint224
print(function_state_mutability(code, '2125b65b')) # pure
Foundry
<a href="https://getfoundry.sh/">Foundy's cast</a> 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
<i>FP/FN</i> - False Positive/False Negative errors; <b>smaller is better</b>
<table> <tr> <td>Dataset</td> <td></td> <td><b><i>evmole</i><b> <a href="benchmark/providers/evmole-rs/"><b><i>rs</i></b></a> · <a href="benchmark/providers/evmole-js/"><b><i>js</i></b></a> · <a href="benchmark/providers/evmole-py/"><b><i>py</i></b></a></td> <td><a href="benchmark/providers/whatsabi/"><b><i>whatsabi</i></b></a></td> <td><a href="benchmark/providers/sevm/"><b><i>sevm</i></b></a></td> <td><a href="benchmark/providers/evm-hound-rs/"><b><i>evmhound</i></b></a></td> <td><a href="benchmark/providers/heimdall-rs/"><b><i>heimdall</i></b></a></td> <td><a href="benchmark/providers/simple/"><b><i>smpl</i></b></a></td> </tr> <tr> <td rowspan="5"><b>largest1k</b><br><sub>1000<br>addresses<br><br>24427<br>functions</sub></td> <td><i>FP <sub>addrs</sub></i></td> <td>1 🥈</td> <td>0 🥇</td> <td>0 🥇</td> <td>75</td> <td>18</td> <td>95</td> </tr> <tr> <td><i>FN <sub>addrs</sub></i></td> <td>0 🥇</td> <td>0 🥇</td> <td>0 🥇</td> <td>40</td> <td>111</td> <td>9</td> </tr> <tr> <td><i>FP <sub>funcs</sub></i></td> <td>192 🥈</td> <td>0 🥇</td> <td>0 🥇</td> <td>720</td> <td>600</td> <td>749</td> </tr> <tr> <td><i>FN <sub>funcs</sub></i></td> <td>0 🥇</td> <td>0 🥇</td> <td>0 🥇</td> <td>191</td> <td>147</td> <td>12</td> </tr> <tr> <td><i>Time</i></td> <td>0.5s · 0.7s · 0.6s</td> <td>3.2s</td> <td>47s<sup>(*)</sup></td> <td>0.5s</td> <td>341s<sup>(*)</sup></td> <td>1.8s</td> </tr> <tr><td colspan="8"></td></tr> <tr> <td rowspan="5"><b>random50k</b><br><sub>50000<br>addresses<br><br>1171102<br>functions</sub></td> <td><i>FP <sub>addrs</sub></i></td> <td>1 🥇</td> <td>43</td> <td>1</td> <td>693</td> <td>3</td> <td>4136</td> </tr> <tr> <td><i>FN <sub>addrs</sub></i></td> <td>9 🥇</td> <td>11</td> <td>10</td> <td>2903</td> <td>4708</td> <td>77</td> </tr> <tr> <td><i>FP <sub>funcs</sub></i></td> <td>3 🥇</td> <td>51</td> <td>3</td> <td>10798</td> <td>29</td> <td>14652</td> </tr> <tr> <td><i>FN <sub>funcs</sub></i></td> <td>10 🥇</td> <td>12</td> <td>11</td> <td>3538</td> <td>6098</td> <td>96</td> </tr> <tr> <td><i>Time</i></td> <td>4.5s · 8.5s · 7.8s</td> <td>54s</td> <td>2472s<sup>(*)</sup></td> <td>6.1s</td> <td>8576s<sup>(*)</sup></td> <td>46s</td> </tr> <tr><td colspan="8"></td></tr> <tr> <td rowspan="5"><b>vyper</b><br><sub>780<br>addresses<br><br>21244<br>functions</sub></td> <td><i>FP <sub>addrs</sub></i></td> <td>0 🥇</td> <td>30</td> <td>0</td> <td>19</td> <td>0</td> <td>185</td> </tr> <tr> <td><i>FN <sub>addrs</sub></i></td> <td>0 🥇</td> <td>780</td> <td>0</td> <td>300</td> <td>780</td> <td>480</td> </tr> <tr> <td><i>FP <sub>funcs</sub></i></td> <td>0 🥇</td> <td>30</td> <td>0</td> <td>19</td> <td>0</td> <td>197</td> </tr> <tr> <td><i>FN <sub>funcs</sub></i></td> <td>0 🥇</td> <td>21244</td> <td>0</td> <td>8273</td> <td>21244</td> <td>12971</td> </tr> <tr> <td><i>Time</i></td> <td>0.4s · 0.6s · 0.4s</td> <td>2.4s</td> <td>57s<sup>(*)</sup></td> <td>0.4s</td> <td>27s<sup>(*)</sup></td> <td>1.1s</td> </tr> </table>function arguments
<i>Errors</i> - when at least 1 argument is incorrect: (uint256,string)
≠ (uint256,bytes)
function state mutability
<i>Errors</i> - Results are not equal (treating view
and pure
as equivalent to nonpayable
)
<i>Errors strict</i> - Results are strictly unequal (nonpayable
≠ view
). Some ABIs mark pure
/view
functions as nonpayable
, so not all strict errors indicate real issues.
See benchmark/README.md for the methodology and commands to reproduce these results
<i>versions: evmole v0.5.1; <a href="https://github.com/shazow/whatsabi">whatsabi</a> v0.14.1; <a href="https://github.com/acuarica/evm">sevm</a> v0.7.3; <a href="https://github.com/g00dv1n/evm-hound-rs">evm-hound-rs</a> v0.1.4; <a href="https://github.com/Jon-Becker/heimdall-rs">heimdall-rs</a> v0.8.4</i>
<sup>(*)</sup>: <b>sevm</b> and <b>heimdall-rs</b> 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