Home

Awesome

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, and state mutability, even for unverified contracts.

Key Features

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

API documentation

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

<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/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="2"><b>largest1k</b><br><sub>24427<br>functions</sub></td> <td><i>Errors</i></td> <td>14.0% 🥇<br><sub>3410</sub></td> <td>31.1%<br><sub>7593</sub></td> <td>58.3%<br><sub>14242</sub></td> </tr> <tr> <td><i>Time</i></td> <td>1.1s · 4.8s · 1.4s</td> <td>342s<sup>(*)</sup></td> <td>0.7s</td> </tr> <tr><td colspan="5"></td></tr> <tr> <td rowspan="2"><b>random50k</b><br><sub>1171102<br>functions</sub></td> <td><i>Errors</i></td> <td>4.5% 🥇<br><sub>52662</sub></td> <td>19.4%<br><sub>227612</sub></td> <td>54.9%<br><sub>643213</sub></td> </tr> <tr> <td><i>Time</i></td> <td>25s · 127 · 32</td> <td>8544s<sup>(*)</sup></td> <td>9.5s</td> </tr> <tr><td colspan="5"></td></tr> <tr> <td rowspan="2"><b>vyper</b><br><sub>21244<br>functions</sub></td> <td><i>Errors</i></td> <td>48.5% 🥇<br><sub>10300</sub></td> <td>100.0%<br><sub>21244</sub></td> <td>56.8%<br><sub>12077</sub></td> </tr> <tr> <td><i>Time</i></td> <td>0.8s · 3.0s · 1.0s</td> <td>28s<sup>(*)</sup></td> <td>0.6s</td> </tr> </table>

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 (nonpayableview). Some ABIs mark pure/view functions as nonpayable, so not all strict errors indicate real issues.

<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/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="3"><b>largest1k</b><br><sub>24427<br>functions</sub></td> <td><i>Errors</i></td> <td>0.0% 🥇<br><sub>0</sub></td> <td>68.1%<br><sub>16623</sub></td> <td>2.1%<br><sub>501</sub></td> <td>25.4%<br><sub>6201</sub></td> <td>2.6%<br><sub>643</sub></td> </tr> <tr> <td><i>Errors strict</i></td> <td>18.7% 🥇<br><sub>4558</sub></td> <td>79.3%<br><sub>19370</sub></td> <td>59.0%<br><sub>14417</sub></td> <td>54.9%<br><sub>13403</sub></td> <td>60.9%<br><sub>14864</sub></td> </tr> <tr> <td><i>Time</i></td> <td>9.5s · 19s · 9.4s</td> <td>3.8s</td> <td>46s<sup>(*)</sup></td> <td>339s<sup>(*)</sup></td> <td>0.7s</td> </tr> <tr><td colspan="6"></td></tr> <tr> <td rowspan="3"><b>random50k</b><br><sub>1160861<br>functions</sub></td> <td><i>Errors</i></td> <td>0.0% 🥇<br><sub>35</sub></td> <td>30.2%<br><sub>351060</sub></td> <td>0.3%<br><sub>3370</sub></td> <td>11.6%<br><sub>134195</sub></td> <td>2.2%<br><sub>24961</sub></td> </tr> <tr> <td><i>Errors strict</i></td> <td>6.7% 🥇<br><sub>77945</sub></td> <td>58.1%<br><sub>674922</sub></td> <td>55.7%<br><sub>646831</sub></td> <td>27.7%<br><sub>321494</sub></td> <td>57.7%<br><sub>670318</sub></td> </tr> <tr> <td><i>Time</i></td> <td>214s · 471s · 240s</td> <td>85s</td> <td>2335s<sup>(*)</sup></td> <td>8151s<sup>(*)</sup></td> <td>9.4s</td> </tr> <tr><td colspan="6"></td></tr> <tr> <td rowspan="3"><b>vyper</b><br><sub>21166<br>functions</sub></td> <td><i>Errors</i></td> <td>0.5% 🥇<br><sub>110</sub></td> <td>100.0%<br><sub>21166</sub></td> <td>76.3%<br><sub>16150</sub></td> <td>100.0%<br><sub>21166</sub></td> <td>1.8%<br><sub>390</sub></td> </tr> <tr> <td><i>Errors strict</i></td> <td>4.2% 🥇<br><sub>888</sub></td> <td>100.0%<br><sub>21166</sub></td> <td>90.2%<br><sub>19092</sub></td> <td>100.0%<br><sub>21166</sub></td> <td>59.6%<br><sub>12610</sub></td> </tr> <tr> <td><i>Time</i></td> <td>10s · 15s · 10s</td> <td>2.4s</td> <td>52s<sup>(*)</sup></td> <td>28s<sup>(*)</sup></td> <td>0.6s</td> </tr> </table>

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