A deterministic transaction classification engine for EVM chains.
ParaLens takes an Ethereum transaction hash and answers one question honestly: what actually happened? It reconstructs the transaction from its execution trace and classifies it — swap, MEV bundle, lending action, liquidation, bridge, exploit — with actor attribution and transaction-local economics.
It is not an indexer and not a label database. Classification is derived from the trace itself, so it works on contracts nobody has labelled yet.
A single entry point drives a seven-layer pipeline
(crates/core/src/analysis.rs):
analyze_transaction(provider, tx_hash, AnalyzeOptions) -> TransactionAnalysis| Layer | Module | Produces |
|---|---|---|
| L0 Reconstruction | reconstruction/ |
raw tx + normalized trace → economic movements |
| L1 Evidence | l1_evidence/ |
canonical Evidence (tx, movements, frames, logs) |
| L2 Ledger | l2_ledger/ |
TVFG token-value-flow graph (nodes, edges, deltas) |
| L3 Structure | l3_structure/ |
scopes, structural primitives, actor attribution |
| L4 Meaning | l4_meaning/ |
Motifs — what patterns are present |
| L5 Composition | l5_composition/ |
one IntentKind — what the transaction was |
| L6 Economic | l6_economic/ |
USD valuation, PnL, costs |
The only network seam is L0. The ChainProvider trait has exactly two
methods — fetch_transaction and fetch_trace — so L1 through L6 are pure
functions of their input. That is what makes the whole engine testable offline
with zero RPC and zero credentials.
Requires Rust stable.
git clone https://github.com/MatheeshaMe/paralens.git
cd paralens
cargo test -p paralens-core --test corpusThat runs the regression corpus against committed fixtures — no API keys, no network. It is the fastest way to see the engine work.
To analyze a live transaction you need a debug_traceTransaction-capable
archive endpoint (standard Alchemy/Infura eth_* URLs will not work):
cp .env.example .env # then set TRACE_RPC_URL or QUICKNODE_RPC_URL
cargo run --bin paralens -- 0x<tx_hash>Dependencies point downward only: wallet → core.
| Crate | Path | Role |
|---|---|---|
paralens-core |
crates/core/ |
The engine: L0–L6, protocol registry, enrichment, pricing, TxReport presentation |
paralens-wallet |
crates/wallet/ |
Cross-transaction layer: FIFO position accounting, reconciliation, wallet reports |
paralens-cli |
apps/cli/ |
Binaries: paralens, fetch_wallet, reconcile, chain_worker |
paralens-server |
apps/server/ |
Stateless HTTP API — POST /analyze → TxReport |
The core library is published under the name paralens, so downstream crates
write use paralens::….
cargo run --bin paralens -- <tx_hash> # analyze one transaction
cargo run --bin paralens -- --report <tx_hash> # human-readable TxReport
cargo run --bin fetch_wallet -- --report <dir> <addr> # wallet bundle (needs ENVIO_API_TOKEN)
cargo run --bin server # HTTP API on $PORT (default 8080)cargo test --workspace # everything
cargo test -p paralens-core --test corpus -- --nocapture # the corpus, offlineEngine tests are organized by layer (crates/core/tests/l2_tvfg,
l3_scope, l4_motif, l5_composition, …). Whole-transaction behavior is
pinned by the corpus.
crates/core/tests/corpus/ is a hermetic regression suite. Each fixture is one
real transaction, committed as its L0 inputs plus an assertion spec:
crates/core/tests/corpus/<category>/<id>/
tx.json RawTransaction (L0 input)
trace.json NormalizedCallFrame (L0 input)
expected.json what the engine must conclude
Fixtures are served by FixtureProvider, so the suite needs no RPC and no
environment variables. A set of global invariants (invariants.rs) is checked
structurally on every fixture.
The corpus only grows. Every newly classified transaction type, and every misclassification fixed, lands a fixture in the same change. Fixtures and invariants are never deleted or weakened to make new work pass — see CONTRIBUTING.md.
- docs/transaction-engine.md — the L0→L6 pipeline in depth
- docs/wallet-intelligence.md — the wallet layer
- docs/http-api.md — HTTP API and the
TxReportschema
Ethereum mainnet is the primary target and is well covered. Base is supported but its registry is thin, so classification quality there is lower. The engine is under active development and pre-1.0: APIs may change between releases.
Contributions are welcome — especially transaction fixtures for cases the engine gets wrong. See CONTRIBUTING.md.
MIT — see LICENSE.