evmole
evmole copied to clipboard
Extracts function selectors and arguments from bytecode, even for unverified contracts
EVMole
This library extracts function selectors and arguments from Ethereum Virtual Machine (EVM) bytecode, even for unverified contracts.
- JavaScript, Rust and Python implementations
- Clean code with zero external dependencies (py & js)
- Faster and more accurate than other existing tools
- Tested on Solidity and Vyper compiled contracts
Usage
JavaScript
$ npm i evmole
import {functionArguments, functionSelectors} from 'evmole'
// Also supported: const e = require('evmole'); e.functionSelectors();
const code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
console.log( functionSelectors(code) )
// Output(list): [ '2125b65b', 'b69ef8a8' ]
console.log( functionArguments(code, '2125b65b') )
// Output(str): 'uint32,address,uint224'
Rust
Documentation available on docs.rs
let code = hex::decode("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256").unwrap();
println!("{:x?}", evmole::function_selectors(&code, 0));
// Output(Vec<[u8;4]>): [[21, 25, b6, 5b], [b6, 9e, f8, a8]]
println!("{}", evmole::function_arguments(&code, &[0x21, 0x25, 0xb6, 0x5b], 0));
// Output(String): uint32,address,uint224
Python
$ pip install evmole --upgrade
from evmole import function_arguments, function_selectors
code = '0x6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256'
print( function_selectors(code) )
# Output(list): ['2125b65b', 'b69ef8a8']
print( function_arguments(code, '2125b65b') )
# Output(str): 'uint32,address,uint224'
Foundry
Foundy's cast uses the Rust implementation of EVMole
$ cast selectors $(cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)
0x06fdde03
0x095ea7b3 address,uint256
0x18160ddd
0x23b872dd address,address,uint256
...
$ cast selectors --resolve $(cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2)
0x06fdde03 name()
0x095ea7b3 address,uint256 approve(address,uint256)
0x18160ddd totalSupply()
0x23b872dd address,address,uint256 transferFrom(address,address,uint256)
...
See examples for more
Benchmark
function selectors
FP/FN - False Positive/False Negative errors; smaller is better
| Dataset | evmole rs · js · py | whatsabi | evm-hound-rs | heimdall-rs | simple | ||
| largest1k 1000 contracts 24427 functions |
FP contracts | 1 🥈 | 0 🥇 | 75 | 18 | 95 | |
| FN contracts | 0 🥇 | 8 | 40 | 102 | 9 | ||
| FP functions | 192 🥈 | 0 🥇 | 720 | 600 | 749 | ||
| FN functions | 0 🥇 | 8 🥈 | 191 | 113 | 12 | ||
| Time | 0.7s · 1.5s · 2.0s | 3.0s | 0.7s | 727.2s | 1.9s | ||
| random50k 50000 contracts 1171102 functions |
FP contracts | 1 🥇 | 43 | 693 | waiting fixes | 4136 | |
| FN contracts | 9 🥇 | 31 | 2903 | 77 | |||
| FP functions | 3 🥇 | 51 | 10798 | 14652 | |||
| FN functions | 10 🥇 | 32 | 3538 | 96 | |||
| Time | 9.8s · 19.6s · 36.4s | 52.3s | 11.5s | 46.3s | |||
| vyper 780 contracts 21244 functions |
FP contracts | 0 🥇 | 30 | 19 | 0 | 185 | |
| FN contracts | 0 🥇 | 780 | 300 | 780 | 480 | ||
| FP functions | 0 🥇 | 30 | 19 | 0 | 197 | ||
| FN functions | 0 🥇 | 21244 | 8273 | 21244 | 12971 | ||
| Time | 0.6s · 0.9s · 1.4s | 2.3s | 0.6s | 17.0s | 1.2s | ||
function arguments
Errors - when at least 1 argument is incorrect: (uint256,string) != (uint256,bytes); smaller is better
| Dataset | evmole rs · js · py | heimdall-rs | simple | |
| largest1k 1000 contracts 24427 functions |
Errors | 15.0%, 3652 🥇 | 42.7%, 10438 | 58.3%, 14242 |
| Time | 1.1s · 7.5s · 15.7s | 731.4s | 0.8s | |
| random50k 50000 contracts 1171102 functions |
Errors | 5.1%, 59484 🥇 | waiting fixes | 54.9%, 643213 |
| Time | 22.6s · 247.0s · 584.7s | 9.5s | ||
| vyper 780 contracts 21244 functions |
Errors | 50.9%, 10805 🥇 | 100.0%, 21244 | 56.8%, 12077 |
| Time | 0.9s · 7.3s · 13.9s | 16.8s | 0.7s | |
See benchmark/README.md for the methodology and commands to reproduce these results
versions: evmole v0.3.3; whatsabi v0.11.0; evm-hound-rs v0.1.4; heimdall-rs v0.7.3
How it works
Short: Executes code with a custom EVM and traces CALLDATA usage.
Long: TODO
License
MIT