Skip to content

Commit be4132b

Browse files
committed
add evmole-go
1 parent ea5507e commit be4132b

17 files changed

Lines changed: 1506 additions & 5 deletions

File tree

.github/workflows/build.yml

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,128 @@ name: build
33
on:
44
push:
55
pull_request:
6+
workflow_dispatch:
7+
inputs:
8+
version:
9+
description: 'Version to release (e.g., 0.9.0)'
10+
required: true
11+
type: string
612

713
env:
814
MATURIN_VERSION: 1.10.0
915

1016
jobs:
17+
prepare-release:
18+
runs-on: ubuntu-latest
19+
if: github.event_name == 'workflow_dispatch'
20+
permissions:
21+
contents: write
22+
steps:
23+
- uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
27+
- name: Validate version format
28+
run: |
29+
if ! echo "${{ inputs.version }}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
30+
echo "Error: Invalid version format. Expected semver like 0.9.0"
31+
exit 1
32+
fi
33+
34+
- name: Check tags do not exist
35+
run: |
36+
if git rev-parse "${{ inputs.version }}" >/dev/null 2>&1; then
37+
echo "Error: Tag ${{ inputs.version }} already exists"
38+
exit 1
39+
fi
40+
if git rev-parse "go/v${{ inputs.version }}" >/dev/null 2>&1; then
41+
echo "Error: Tag go/v${{ inputs.version }} already exists"
42+
exit 1
43+
fi
44+
45+
- name: Install Rust
46+
uses: dtolnay/rust-toolchain@stable
47+
with:
48+
targets: wasm32-unknown-unknown
49+
50+
- name: Update version in Cargo.toml
51+
run: sed -i 's/^version = ".*"/version = "${{ inputs.version }}"/' Cargo.toml
52+
53+
- name: Update version in javascript/package.json
54+
run: |
55+
sed -i 's/"version": ".*"/"version": "${{ inputs.version }}"/' javascript/package.json
56+
57+
- name: Build WASM
58+
run: cargo build --target wasm32-unknown-unknown --release --features wasm
59+
60+
- name: Compress WASM
61+
run: gzip -9 -c target/wasm32-unknown-unknown/release/evmole.wasm > go/wasm/evmole.wasm.gz
62+
63+
- name: Configure Git
64+
run: |
65+
git config user.name "github-actions[bot]"
66+
git config user.email "github-actions[bot]@users.noreply.github.com"
67+
68+
- name: Commit and Tag
69+
run: |
70+
git add Cargo.toml Cargo.lock javascript/package.json go/wasm/evmole.wasm.gz
71+
git commit -m "release ${{ inputs.version }}"
72+
git tag "${{ inputs.version }}"
73+
git tag "go/v${{ inputs.version }}"
74+
git push origin HEAD:${{ github.ref_name }}
75+
git push origin "${{ inputs.version }}"
76+
git push origin "go/v${{ inputs.version }}"
77+
1178
rust-test:
1279
runs-on: ubuntu-latest
80+
needs: [prepare-release]
81+
if: always() && (needs.prepare-release.result == 'success' || needs.prepare-release.result == 'skipped')
1382
steps:
1483
- uses: actions/checkout@v4
84+
with:
85+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
86+
- name: Install Rust
87+
uses: dtolnay/rust-toolchain@stable
1588
- name: Test and Clippy
1689
run: |
1790
cargo fmt --check
1891
cargo test
1992
cargo clippy --all-features -- -D warnings
2093
94+
go-test:
95+
runs-on: ubuntu-latest
96+
needs: rust-test
97+
if: always() && needs.rust-test.result == 'success'
98+
strategy:
99+
matrix:
100+
go-version: ['1.25', 'stable']
101+
steps:
102+
- uses: actions/checkout@v4
103+
with:
104+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
105+
- name: Install Rust
106+
uses: dtolnay/rust-toolchain@stable
107+
with:
108+
targets: wasm32-unknown-unknown
109+
- name: Install Go
110+
uses: actions/setup-go@v5
111+
with:
112+
go-version: ${{ matrix.go-version }}
113+
- name: Build WASM
114+
working-directory: go
115+
run: make wasm
116+
- name: Run tests
117+
working-directory: go
118+
run: make test
119+
21120
javascript:
22121
runs-on: ubuntu-latest
23122
needs: rust-test
123+
if: always() && needs.rust-test.result == 'success'
24124
steps:
25125
- uses: actions/checkout@v4
126+
with:
127+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
26128
- name: Install Node
27129
uses: actions/setup-node@v4
28130
with:
@@ -53,6 +155,7 @@ jobs:
53155

54156
python-wheel:
55157
needs: rust-test
158+
if: always() && needs.rust-test.result == 'success'
56159
strategy:
57160
matrix:
58161
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
@@ -69,6 +172,8 @@ jobs:
69172
runs-on: ${{ matrix.platform.runner }}
70173
steps:
71174
- uses: actions/checkout@v4
175+
with:
176+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
72177
- name: Install python
73178
uses: actions/setup-python@v5
74179
with:
@@ -94,8 +199,11 @@ jobs:
94199
python-sdist:
95200
runs-on: ubuntu-latest
96201
needs: rust-test
202+
if: always() && needs.rust-test.result == 'success'
97203
steps:
98204
- uses: actions/checkout@v4
205+
with:
206+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
99207
- name: Install python
100208
uses: actions/setup-python@v5
101209
with:
@@ -114,11 +222,15 @@ jobs:
114222
compression-level: 0
115223

116224
python-sdist-test:
117-
# ubuntu-latest image (https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md) have Rust installed
118225
runs-on: ubuntu-latest
119226
needs: python-sdist
227+
if: always() && needs.python-sdist.result == 'success'
120228
steps:
121229
- uses: actions/checkout@v4
230+
with:
231+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }}
232+
- name: Install Rust
233+
uses: dtolnay/rust-toolchain@stable
122234
- name: Install python
123235
uses: actions/setup-python@v5
124236
with:
@@ -134,15 +246,19 @@ jobs:
134246

135247
release:
136248
runs-on: ubuntu-latest
137-
if: startsWith(github.ref, 'refs/tags/') # only on tagged releases
138-
needs: [javascript, python-wheel, python-sdist, python-sdist-test]
249+
if: github.event_name == 'workflow_dispatch'
250+
needs: [javascript, python-wheel, python-sdist, python-sdist-test, go-test]
139251
permissions:
140252
contents: write
141253
id-token: write
142254
steps:
143255
- uses: actions/checkout@v4
144256
with:
145257
fetch-depth: 0 # need tags to generate release notes
258+
ref: ${{ inputs.version }}
259+
260+
- name: Install Rust
261+
uses: dtolnay/rust-toolchain@stable
146262

147263
- name: Publish rust
148264
env:
@@ -177,7 +293,8 @@ jobs:
177293
- name: Github Release
178294
uses: softprops/action-gh-release@v2
179295
with:
180-
name: Release ${{ github.ref_name }}
296+
tag_name: ${{ inputs.version }}
297+
name: Release ${{ inputs.version }}
181298
draft: false
182299
prerelease: false
183300
body_path: changelog.md

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ dist/
1212
target/
1313
.aider*
1414
.gradle
15+
16+
# Go WASM - use compressed version only
17+
go/wasm/evmole.wasm

Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ pyo3 = { version = "0.27", features = ["extension-module"], optional = true }
2121
wasm-bindgen = { version = "0.2", optional = true }
2222
serde-wasm-bindgen = { version = "0.6", optional = true }
2323
serde = { version = "1.0", features = ["derive"], optional = true }
24+
serde_json = { version = "1.0", optional = true }
2425

2526
[features]
2627
serde = ["dep:serde"]
2728
python = ["dep:pyo3"]
2829
javascript = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "serde"]
30+
wasm = ["serde", "dep:serde_json"]
2931

3032
# for dev
3133
trace_selectors = []

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
[![npm](https://img.shields.io/npm/v/evmole)](https://www.npmjs.com/package/evmole)
55
[![Crates.io](https://img.shields.io/crates/v/evmole?color=e9b44f)](https://crates.io/crates/evmole)
66
[![PyPI](https://img.shields.io/pypi/v/evmole?color=006dad)](https://pypi.org/project/evmole)
7+
[![Go](https://img.shields.io/badge/go-pkg-00ADD8)](https://pkg.go.dev/github.com/cdump/evmole/go)
78

89
EVMole is a powerful library that extracts information from Ethereum Virtual Machine (EVM) bytecode, including [function selectors](https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector), arguments, [state mutability](https://docs.soliditylang.org/en/latest/contracts.html#state-mutability), and storage layout, even for unverified contracts.
910

1011

1112
## Key Features
1213

13-
- Multi-language support: Available as [JavaScript](#javascript), [Rust](#rust), and [Python](#python) libraries.
14+
- Multi-language support: Available as [JavaScript](#javascript), [Rust](#rust), [Python](#python), and [Go](#go) libraries.
1415
- High accuracy and performance: [Outperforms](#benchmark) existing tools.
1516
- Broad compatibility: Tested with both Solidity and Vyper compiled contracts.
1617
- Lightweight: Clean codebase with minimal external dependencies.
@@ -84,6 +85,39 @@ print( contract_info(code, selectors=True, arguments=True, state_mutability=True
8485
# ...
8586
```
8687

88+
### Go
89+
[API documentation](./go/#api-reference)
90+
```sh
91+
$ go get github.com/cdump/evmole/go
92+
```
93+
```go
94+
package main
95+
96+
import (
97+
"context"
98+
"encoding/hex"
99+
"fmt"
100+
101+
"github.com/cdump/evmole/go"
102+
)
103+
104+
func main() {
105+
code, _ := hex.DecodeString("6080604052348015600e575f80fd5b50600436106030575f3560e01c80632125b65b146034578063b69ef8a8146044575b5f80fd5b6044603f3660046046565b505050565b005b5f805f606084860312156057575f80fd5b833563ffffffff811681146069575f80fd5b925060208401356001600160a01b03811681146083575f80fd5b915060408401356001600160e01b0381168114609d575f80fd5b80915050925092509256")
106+
107+
info, _ := evmole.ContractInfo(context.Background(), code, evmole.Options{
108+
Selectors: true,
109+
Arguments: true,
110+
StateMutability: true,
111+
})
112+
113+
for _, fn := range info.Functions {
114+
fmt.Printf("%s: %s @ %d\n", fn.Selector, *fn.Arguments, fn.BytecodeOffset)
115+
}
116+
// 2125b65b: uint32,address,uint224 @ 52
117+
// b69ef8a8: @ 68
118+
}
119+
```
120+
87121
### Foundry
88122
<a href="https://getfoundry.sh/">Foundy's cast</a> uses the Rust implementation of EVMole
89123
```sh

benchmark/hetzner-bench.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Run evmole benchmarks on Hetzner Cloud dedicated vCPU servers.
8383
8484
Required:
8585
-b, --benchmark <name> Benchmark to run: selectors|arguments|mutability|storage|flow|all
86+
Can be specified multiple times: -b selectors -b arguments
8687
8788
Server (one of):
8889
-k, --ssh-key <name> SSH key name - creates new server (must exist in Hetzner account)

go/Makefile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.PHONY: build test bench clean wasm
2+
3+
# Build WASM module and copy to go/wasm/
4+
wasm:
5+
cargo build --target wasm32-unknown-unknown --release --features wasm --manifest-path ../Cargo.toml
6+
gzip -9 -c ../target/wasm32-unknown-unknown/release/evmole.wasm > wasm/evmole.wasm.gz
7+
8+
# Run tests
9+
test:
10+
go test -v ./...
11+
12+
# Run benchmarks
13+
bench:
14+
go test -bench=. -benchmem ./...
15+
16+
# Build Go package (verify compilation)
17+
build:
18+
go build ./...
19+
20+
# Clean build artifacts
21+
clean:
22+
rm -f wasm/evmole.wasm.gz
23+
go clean ./...
24+
25+
# Full rebuild: build WASM and run tests
26+
all: wasm test

0 commit comments

Comments
 (0)