<h1>
<p align="center">
  <br>DexTrades 🦄 
</p >
</h1>

<p align="center">
A Python library for streaming DEX trades from RPC nodes.
</p>

<p align="center">
  <a href="https://pypi.org/project/dextrades">
    <img src="https://img.shields.io/pypi/v/dextrades.svg?label=pypi&logo=PyPI&logoColor=white" alt="PyPI">
  </a>
  <a href="https://opensource.org/licenses/MIT">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT">
  </a>
</p>

## ✨ Features

* **Direct On-Chain Data**: Pulls event logs directly from a given RPC URL, requiring no indexers or third-party APIs. It currently decodes swaps from Uniswap V2 and V3.

* **Data Enrichment Pipeline**: The library can enrich raw log data with:
    * Token metadata (symbols, decimals) for readable amounts.
    * Block timestamps for each swap.
    * USD values calculated using Chainlink ETH/USD price feeds at the swap's block height.

* **Rust Core**: Built with a Rust backend (`PyO3`, `alloy`) for processing. It implements RPC provider racing, a circuit breaker, and automatic retries for connection resilience.

* **Friendly Python API**: Provides an `async` generator to stream trades. Enrichments are controlled via boolean flags. Supports streaming individual swaps or Apache Arrow batches.

##  📦 Installation


Using `uv` (recommended):
```bash
uv add dextrades
````

Or with `pip`:

```bash
pip install dextrades
```

## 💻 Usage

The `Client` manages connections to one or more RPC endpoints. The `stream_swaps` method returns an async iterator of swap events.

```python
import asyncio
import dextrades

urls = [
    "https://eth-pokt.nodies.app",
    "https://ethereum.publicnode.com",
]
with dextrades.Client(urls) as client:
    # Stream a small block range; normalized token amounts included
    async for swap in client.stream_swaps(
        ["uniswap_v2", "uniswap_v3"],
        17000003, 17000003,
        batch_size=1,
        enrich_timestamps=True,
        enrich_usd=True,
    ):
        print(
            swap.get("dex_protocol"),
            swap.get("token_sold_symbol"), swap.get("token_sold_amount"),
            "→",
            swap.get("token_bought_symbol"), swap.get("token_bought_amount"),
            "USD:", swap.get("value_usd"),
        )
```

### Example Output

```
time                 dex           bought           sold              value_usd  trader  hash  
-----------------------------------------------------------------------------------------------------------------------------
2023-04-08 01:58:47  Uniswap V2    0.0529  WETH     98.9990  USDC        $99.00  0x5eA7  0x37f7
2023-04-08 01:58:47  Uniswap V2    0.0398  XMON      0.0529  WETH        $98.63  0x5eA7  0x37f7
2023-04-08 01:58:47  Uniswap V2    0.0452  WETH      0.7000  QNT         $84.38  0x4a30  0x5428
2023-04-08 01:58:47  Uniswap V2    3.2402  WETH      2.9994  PAXG     $6,045.62  0xdBC2  0x8f46
```

## 🗺️ Roadmap

- [x] Uniswap V2
- [x] Uniswap V3
- [x]  and deduplication
- [x] Enrichments: 
  - [x] token metadata
  - [x] trade direction
  - [x] timestamps
  - [x] USD values via Chainlink
  - [x] USD values via stablecoin passthrough 
- [x] RPC provider
  - [x] racing
  - [x] retries
  - [x] circuit breakers
  - [x] sharded `getLogs`
- [x] Python API
- [x] CLI
- [x] example and demo
- [x] benchmarks
- [ ] additional enrichments:
  - [ ] trader balance
  - [ ] Uniswap V3 Quoter fallback for non-WETH/stable tokens
- [ ] Chainlink Feed Registry (USD feeds) and multi-chain aggregator addresses
- [ ] CLI UX polish (enrichment flags, simple table mode)
- [ ] Light metrics: stage counters and provider health snapshot
- [ ] Additional DEX protocols
- [ ] Optional persistent caches and Parquet/Polars export helpers
