Introducing Sigvex: Multi-Runtime Smart Contract Security for EVM and Solana
Smart contract security tooling has a few persistent problems. Most tools analyze source code, but source code is not what executes on-chain — compiled bytecode is. Most tools cover EVM only, but a large fraction of on-chain value now lives on Solana and other runtimes. And most tools flag potential vulnerabilities without distinguishing between theoretical and confirmed exploitability.
Sigvex is a Rust toolkit that addresses these directly. It decompiles and analyzes smart contracts at the bytecode level across EVM (Ethereum, Arbitrum, Optimism, Base, Polygon, BSC) and SVM (Solana eBPF/BPF programs), and where possible, validates findings with automated exploit generation.
What It Does
Bytecode-native decompilation
Analysis starts from deployed bytecode. No source code required, no compiler assumptions.
This matters in several situations. Compiler optimizations can remove safety checks present in source — Solidity 0.8+ adds checked arithmetic, but under specific optimization conditions those checks can be eliminated. Unverified contracts have no source to analyze; Sigvex treats them identically to verified ones. And when a UUPS proxy upgrades its implementation, source-level audits of the previous version become irrelevant — bytecode analysis always reflects what currently executes.
The decompilation pipeline runs through multiple intermediate representations:
Bytecode → LIR (Low-level IR) → HIR (High-level IR) → Solidity / Yul / Rust
LIR makes the EVM stack explicit as named variables for precise data flow tracking. HIR recovers structured control flow — loops, conditionals, function boundaries — and performs type inference to recover address, bool, bytes32, and mapping types through SSA-form passes including constant propagation and dead code elimination. The backends then produce readable Solidity (EVM) or Rust-like output (SVM).
361 security detectors
Detectors span three runtimes — 136 EVM, 192 SVM, and 33 ZK — organized by severity:
| Severity | Count | Examples |
|---|---|---|
| Critical | 85 | Reentrancy (classic, cross-function, read-only), arbitrary storage writes, delegatecall misuse, UUPS initialization, storage collision, arbitrary CPI, missing signer checks |
| High | 138 | Integer overflow, flash loan attack vectors, oracle manipulation, MEV vulnerabilities, Chainlink staleness, sandwich attacks, account validation gaps, PDA seed collisions |
| Medium | 82 | Timestamp dependence, signature replay, TWAP manipulation, front-running patterns, type cosplay, ZK circuit under-constraining |
| Low | 56 | ERC/SPL standard violations, selfdestruct usage, floating pragma, gas optimization, ZK proof size inefficiencies |
All 361 detectors run by default. Analysis can be scoped to a minimum severity threshold for faster triage — for example, running only critical and high severity detectors reduces analysis time for large contracts.
Each finding includes severity, confidence score, and where possible, a concrete exploit scenario generated by the symbolic engine. The exploitable label is set when automated exploit generation produces a confirmed working attack in simulation.
Clean architecture with runtime isolation
The codebase uses a layered clean architecture where each level may only depend on the levels below it. EVM and SVM runtimes are isolated from each other — they share abstract interfaces but no implementation details. A unified API gateway presents a single interface regardless of which runtime a contract belongs to.
The layers separate concerns: foundation utilities sit at the bottom, domain types and the decompilation pipeline in the middle, vulnerability detection and exploit generation above those, and infrastructure (RPC, storage) and presentation (CLI, APIs) at the top. This means a vulnerability class can be implemented for EVM and independently for SVM without coupling between runtimes.
Proxy resolution
Proxy contracts are resolved automatically before analysis:
- EIP-1967 (transparent and UUPS proxies)
- EIP-1167 (minimal clone proxies)
- EIP-2535 (Diamond multi-facet proxies)
When a proxy is detected, both the proxy contract and the current implementation are analyzed together. Storage layout conflicts between proxy and implementation are a distinct detector class.
Getting Started
# Build release binaries
cargo build --release
# Decompile a contract to Solidity
./target/release/sigvex decompile 0xContractAddress --network ethereum
# Run security analysis
./target/release/sigvex analyze 0xContractAddress --network ethereum
# Scope analysis to critical and high severity only
./target/release/sigvex analyze 0xContractAddress --network ethereum --min-severity high
The REST API accepts bytecode directly or on-chain addresses:
# Analyze by address
curl -X POST http://localhost:8080/api/v1/analyze \
-H "Content-Type: application/json" \
-d '{"address": "0x...", "network": "ethereum"}'
# Analyze raw bytecode (no network required)
curl -X POST http://localhost:8080/api/v1/decompile \
-H "Content-Type: application/json" \
-d '{"bytecode": "0x6080604052..."}'
Analysis results are stored in a structured flat-file database organized by data mutability:
contract-db/<network>/<address>/bytecode/ # Immutable: decompiled source, functions, variables
contract-db/<network>/<address>/state/ # Mutable: storage slots, dynamic state
contract-db/<network>/<address>/analysis/ # Derived: findings, exploits, call graphs
Design Decisions
A few choices worth explaining:
Rust throughout. The analysis pipeline runs on untrusted bytecode at scale. Memory safety and predictable performance matter. Rust’s module system enforces the architectural boundaries — lower-level components cannot accidentally depend on higher-level concerns.
Flat-file storage, not a database. Analysis results are JSON files in a predictable directory structure. This makes results inspectable, diffable, and portable without a database dependency. The same structure works locally and on S3 or Azure Blob Storage.
Separate storage directories by mutability. Bytecode data never changes after deployment. State data changes every block. Analysis results are derived from both and can be regenerated. Separating these prevents stale analysis results from contaminating immutable data and makes cache invalidation tractable.
Confidence scores, not just severity. A critical finding with 0.60 confidence warrants different attention than a critical finding with 0.97 confidence. The score reflects how precisely the static pattern matches known exploitable behavior, and rises to 1.0 when exploit generation confirms the finding in simulation.
Roadmap
The current focus is deeper symbolic execution for automated exploit validation across more vulnerability classes, cross-contract taint tracking for multi-hop attack chains, and SVM detector parity with EVM. The WASM fuzzer brings coverage-guided fuzzing to the browser for client-side security testing without a backend dependency.