Metadata-Version: 2.4
Name: boxtime
Version: 1.0.1
Summary: Cointime economics framework implementation for the Ergo blockchain.
Project-URL: Repository, https://github.com/4EYESConsulting/boxtime
Author-email: Luca D'Angelo <ldgaetano@protonmail.com>
License: MIT
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.10
Requires-Dist: aiohttp
Requires-Dist: coingecko-sdk
Requires-Dist: pydantic>=2
Requires-Dist: requests
Provides-Extra: test
Requires-Dist: aioresponses; extra == 'test'
Requires-Dist: pytest; extra == 'test'
Requires-Dist: pytest-asyncio; extra == 'test'
Description-Content-Type: text/markdown

# boxtime

Cointime economics framework implementation for the Ergo blockchain.

## Description

Boxtime is a Python library that implements the [Cointime Economics](https://insights.glassnode.com/introducing-cointime-economics/) framework for Ergo. It connects to one or more Ergo nodes to compute coinblocks created, destroyed, and stored — using concurrent async I/O for high throughput. It optionally enriches the data with ERG/USD prices from CoinGecko for plotting and analysis.

## Installation

```bash
pip install boxtime
```

## Prerequisites

- **Python 3.10+**
- **Ergo Node** — one or more running nodes with the extra blockchain indexer enabled (`ergo.node.extraIndex = true`).
- **CoinGecko API key** *(optional)* — a free Demo API key from [coingecko.com/en/api](https://www.coingecko.com/en/api). Required only for price-related methods.

## Quickstart

### Basic cointime metrics

```python
from boxtime import Cointime

# Single node (default: http://127.0.0.1:9053)
ct = Cointime()

# Coinblocks created at a single height (= circulating supply in nanoERGs)
cbc = ct.coinblocks_created(500_000)

# Coinblocks destroyed at a single height
cbd = ct.coinblocks_destroyed(500_000)

# Coinblocks stored (CBC - CBD)
cbs = ct.coinblocks_stored(500_000)

# Totals over a range — runs concurrently
total_cbc = ct.total_coinblocks_created(500_000, 500_100)
```

### Multi-node with concurrency

Distribute requests across multiple nodes with round-robin load balancing:

```python
ct = Cointime(
    node_urls=["http://node1:9053", "http://node2:9053"],
    max_concurrent=50,  # default
)

# Same sync API — concurrency handled internally
# Works in scripts, Jupyter notebooks, async frameworks, etc.
total_cbc = ct.total_coinblocks_created(0, 1_000_000)
total_cbd = ct.total_coinblocks_destroyed(0, 1_000_000)
total_cbs = ct.total_coinblocks_stored(0, 1_000_000)
```

### Price-enriched data (for plotting)

Pass a CoinGecko API key to unlock price methods:

```python
ct = Cointime(
    node_urls=["http://127.0.0.1:9053"],
    coingecko_api_key="CG-your-demo-key",
)

# Current ERG/USD spot price
price = ct.get_price()

# Price history between two block heights
history = ct.get_price_history(500_000, 510_000)

# Coinblocks created with matched price data — ready to plot
data_points = ct.get_coinblocks_created(500_000, 510_000)
for dp in data_points:
    print(dp.height, dp.timestamp, dp.value, dp.price)
```

Each `CointimeDataPoint` bundles `height`, `timestamp`, `value` (cointime metric in nanoERG-blocks), and `price` (ERG/USD). Only block timestamps that exactly match a CoinGecko price timestamp are included.

## API Overview

### `Cointime(node_urls, coingecko_api_key, max_concurrent)`

| Parameter | Default | Description |
| --- | --- | --- |
| `node_urls` | `["http://127.0.0.1:9053"]` | List of Ergo node URLs for round-robin load balancing |
| `coingecko_api_key` | `None` | CoinGecko Demo API key (enables price methods) |
| `max_concurrent` | `50` | Maximum concurrent requests |

**Primitive metrics** (single height):
- `coinblocks_created(height)` — circulating supply at height
- `coinblocks_destroyed(height)` — sum of `input.value × lifespan` for all inputs
- `coinblocks_stored(height)` — CBC − CBD

**Aggregate metrics** (height range, inclusive — runs concurrently):
- `total_coinblocks_created(start_height, end_height)`
- `total_coinblocks_destroyed(start_height, end_height)`
- `total_coinblocks_stored(start_height, end_height)`

**Price methods** (require CoinGecko API key):
- `get_price()` — current ERG/USD spot price
- `get_price_history(start_height, end_height)` — `List[PricePoint]`

**Convenience methods** (require CoinGecko API key):
- `get_coinblocks_created(start_height, end_height)` — `List[CointimeDataPoint]`
- `get_coinblocks_destroyed(start_height, end_height)` — `List[CointimeDataPoint]`
- `get_coinblocks_stored(start_height, end_height)` — `List[CointimeDataPoint]`

### `ErgoNodeClient(session, node_url)` — async Ergo node client

All methods are `async def`. Requires an `aiohttp.ClientSession`:

```python
import asyncio
import aiohttp
from boxtime import ErgoNodeClient

async def main():
    async with aiohttp.ClientSession() as session:
        node = ErgoNodeClient(session, "http://127.0.0.1:9053")
        info = await node.get_node_info()
        tx = await node.get_transaction("tx_id_here")
        emission = await node.get_emission_at(500_000)

asyncio.run(main())
```

### `CoinGeckoClient(api_key)` — price data

Synchronous client using the `coingecko-sdk`:

- `get_current_price(vs_currency)` — current ERG price
- `get_price_history(days, vs_currency)` — price history
- `get_price_history_range(from_ts, to_ts, vs_currency)` — price history between timestamps
- `get_price_at_date(date, vs_currency)` — price on a specific date

## Development

```bash
# Install with test dependencies
pip install -e ".[test]"

# Run tests
pytest
```

## License

This project is licensed under the [MIT License](LICENSE).
