dexamine earns its place as a normalization layer: it combines signed token flows, receipt-level ordering and pool state with transaction gas and block fields. Decoding itself is cheap. The paper makes no performance promise: "no throughput advantage over other extraction tools is claimed here," and processing rates vary with endpoint latency and capacity, batch size and metadata cache misses. Attached to an existing extraction stack, it works. Building a historical Uniswap dataset on top of it exposes two hazards that the paper identifies without fully resolving. Metadata defaults to the latest block. Its reported log index also belongs to the receipt, which prevents a direct join to Ethereum's block-wide index.
What the package covers
This single-authored software paper describes version 1.1.0, released under GPL-3.0-or-later. dexamine covers Uniswap v2 and v3 swaps, mints and burns on Ethereum mainnet. The input is a transaction position, comprising a block number and transaction index, together with a protocol and an optional pool address. The output can be the raw node payloads or one flat row for each event. Flat rows include token symbols and decimals, quantities scaled from integers, post-event pool state and transaction-level gas fields.
Two runtime dependencies handle the external work. Requests supplies JSON-RPC access, while web3.py handles contract metadata calls and checksumming. The requirements are Python 3.10 or newer, web3.py 6.15.0 or newer and Requests 2.31.0 or newer. Continuous integration spans Python 3.10 through 3.13.
Protocol-specific parsing carries most of the substance. Uniswap v2 swap logs omit the resulting reserves. Hansson therefore obtains the post-trade marginal price before fees from the reserve ratio in the immediately preceding Sync event. The parser checks that the events are adjacent and skips the swap when they are not. Uniswap v3 records the square-root price, tick and active liquidity. dexamine uses those fields to derive price and virtual reserves, a description of the local curve rather than the pool balances. When active liquidity is zero, the derived values are reported as missing.
There is no empirical sample. End-to-end evidence comes from one transaction, supplemented by offline tests using recorded responses and constructed logs. A separate verification script checks the transaction and block identity, decodes archived metadata calls, then independently derives every swap amount and pool state through ABI decoding and high-precision decimal arithmetic. Both checks run in continuous integration without a node. An optional suite tests the public interface against a live endpoint.
The paper calls this transaction an illustration, including in the section heading, rather than presenting it as a validation study. Version 1 was also used to produce data for the author's own SSRN working paper on price discovery in constant product markets.
Can a block timestamp order four swaps?
The example uses transaction index 31 in block 12,561,528, dated 3 June 2021. Four v3 swaps occur across three pools at zero-based receipt positions 3, 6, 10 and 13. Transfers and other logs ignored by the v3 parser occupy the gaps.
The signed flows reveal the route, with a positive amount indicating tokens entering the pool. First, 5.000000 WETH enters USDC/WETH and 14008.253925 USDC leaves. That USDC then enters DAI/USDC, producing 13983.724002 DAI. The DAI enters DAI/WETH and 4.999395 WETH exits. Finally, that WETH enters USDC/WETH again and 14003.702900 USDC leaves. Five WETH went in; 14,003.70 USDC came out.
The paper goes no further because its destination labels "do not establish trader identity or the transaction's economic purpose." Positions 3 and 13 revisit the same pool and must remain distinct observations. Each carries its own post-swap state: 2802.766431 USDC per WETH at tick 196936, followed by 2802.194884 at tick 196938.
A block timestamp cannot reconstruct this sequence.
Price direction creates another easy mistake. Output prices are quoted as token 0 per token 1. Across these two observations, the quoted USDC per WETH declines while the tick increases. Both readings are correct because the measures move in opposite directions.
The index presents a separate joining hazard. Each flat row contains receipt_log_index, meaning the event's position within the receipt's log list. Hansson explicitly distinguishes it from Ethereum's block-wide logIndex. Joining dexamine output to a dataset that carries the block-wide field will silently mismatch records. The warning belongs in the paper, though users will still get caught by it.
Gas makes the third trap. The transaction consumed 476,588 gas at an effective gas price of 40 gwei, and dexamine repeats those values on all four rows. The paper states the consequence plainly: "Summing those fields across swaps would count the same transaction cost four times." We supplied the following conversion outside the paper. At 476,588 gas and 40 gwei, the cost is 0.0191 ETH. Using the fourth swap's post-swap price of 2802.194884 USDC per WETH gives about 53 USDC, roughly 38 bps of the 14,003.70 USDC received. Naively adding the values across the four rows produces 152 bps.
Historical metadata remains the user's responsibility
Pool and token metadata are fetched at the latest block and cached. As the paper explains, repeated runs can change when token metadata changes even if the historical logs remain fixed. Decimal precision determines how integer token amounts are scaled, so the issue affects quantities as well as labels.
Hansson fixes this for the distributed example. Metadata captured through contract calls at the recorded block is loaded with a resolver method named MetadataResolver.seed. Archived RPC responses are then replayed, and any unexpected HTTP access is rejected. The offline reproduction is therefore pinned.
A user's own 2021 backfill follows the default path. The paper assigns provenance for seeded values to the caller, while absent entries continue to query the endpoint. Retaining input responses and metadata, as the paper recommends, supports replication after those materials have been captured.
The node requirement is comparatively light. An endpoint must return the requested historical blocks and receipts and answer metadata calls. According to the paper, the parser has no need for historical contract-state queries.
Transaction discovery happens elsewhere
Discovery sits outside the package's intended scope. Users must supply the block number and transaction index themselves. The paper directs them to Ethereum ETL, cryo, Graph Node or TrueBlocks for selection. Because reorgs can invalidate recent positions, transaction hashes should be retained alongside them.
Destination labels fall into three buckets: contract_creation, uniswap_router, other_contract. They use only the top-level destination. dexamine performs no internal call reconstruction and makes no claim about trader identity or extractable value. Every example row is other_contract. Quantities are represented as floats, so they do not provide integer accounting. The paper says as much and notes that the original logs preserve the integer quantities for later recovery.
We could not test the package on our own data. It requires Ethereum transaction receipts, event logs, in-block transaction ordering and pool-contract metadata. Our crypto holdings consist of centralized-exchange price bars from around 2018. Those bars cannot reproduce a single pool-level swap or mint.
The paper's verification covers the one recorded transaction, constructed logs and an independent decimal re-derivation, all focused on that same transaction. It gives no evidence about coverage across pools and does not measure how often the v2 Sync adjacency check silently drops an event. A useful test would parse a month from a few busy pools, reconcile amounts, ordering and post-swap state against the Uniswap subgraph or a cryo extract, then report the drop count.
Until someone performs that test, dexamine remains a parser I would use rather than one I would trust for a large backfill. The example establishes that the interface and ordering convention work. Coverage at scale remains untested.