Metadata-Version: 2.5
Name: zora-coins
Version: 0.1.0
Summary: Unofficial typed Python SDK for Zora Coins: every API endpoint, trade quotes, coin creation calls, and an onchain creator/referral rewards indexer.
Project-URL: Homepage, https://github.com/pgalyen1987/zora-coins-py
Project-URL: Issues, https://github.com/pgalyen1987/zora-coins-py/issues
Author-email: Rebel Studios Software <contact@rebelstudiossoftware.com>
License-Expression: MIT
License-File: LICENSE
Keywords: base,creator coins,onchain,sdk,web3,zora,zora coins
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.25
Requires-Dist: pydantic>=2.5
Provides-Extra: dev
Requires-Dist: datamodel-code-generator>=0.25; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# zora-coins

**Typed Python SDK for [Zora Coins](https://docs.zora.co/coins)**, plus an onchain indexer for the creator and referral rewards Zora pays on every trade.

Zora's official SDK is TypeScript-only. `zora-coins` covers the same public API from Python with typed models generated from Zora's own OpenAPI spec, and adds something neither the SDK nor the API offers: **how much an address has actually earned** as a creator, platform referrer or trade referrer, read directly from Base.

> Unofficial and community-maintained. Not affiliated with Zora.

```bash
pip install zora-coins
```

Python 3.10+. Dependencies: `httpx`, `pydantic`.

## Quick start

```python
from zora_coins import ZoraCoins, ListType, eth, erc20

with ZoraCoins() as zora:                      # ZoraCoins(api_key="...") for higher rate limits
    coin = zora.get_coin("0x0b8590d3c0b1ee6c797e184a4afbb15f8f58a46b")
    print(coin.name, coin.market_cap, coin.unique_holders, coin.creator_profile.handle)

    for c in zora.iter_explore(ListType.top_volume_24_h, limit=10):   # follows pagination
        print(c.symbol, c.volume24h)

    for holder in zora.iter_coin_holders(coin.address, limit=100):
        print(holder.owner_address, holder.balance)

    # Build a trade. Nothing is signed or sent: pass quote.call to your wallet library.
    quote = zora.quote_trade(eth(), erc20(coin.address), amount_in=10**15, sender="0xYourWallet")
    tx = {"to": quote.call.target, "data": quote.call.data, "value": int(quote.call.value)}
```

Async works the same way:

```python
from zora_coins import AsyncZoraCoins

async with AsyncZoraCoins() as zora:
    profile = await zora.get_profile("rebelstudios")
    async for coin in zora.iter_profile_coins("rebelstudios"):
        print(coin.symbol)
```

## What's covered

| Area | Methods |
|---|---|
| Coins | `get_coin`, `get_coins`, `get_coin_holders` / `iter_coin_holders`, `get_coin_swaps` / `iter_coin_swaps`, `get_coin_comments` / `iter_coin_comments`, `get_coin_price_history`, `get_coins_list` / `iter_coins_list`, `get_token_info` |
| Explore | `explore` / `iter_explore` (all 25 list types: `TOP_GAINERS`, `TOP_VOLUME_24H`, `NEW`, `MOST_VALUABLE_CREATORS`, `TRENDING_ALL`…), `search`, `get_trader_leaderboard`, `get_featured_creators`, `get_trend_coin`, `get_trends_by_name`, live streams |
| Profiles | `get_profile`, `get_profile_coins` / `iter_profile_coins` (filter by `platform_referrer`), `get_profile_balances` / `iter_profile_balances`, `get_profile_social`, `get_profile_by_social_handle`, `get_wallet_trade_activity` |
| Transactions | `quote_trade` (buy/sell calldata with optional trade `referrer`), `create_content_coin` (creation calls + predicted address, optional `platform_referrer`), pool configs |

Every response is a pydantic model (`zora_coins.models`). Rate limits (429) and 5xx responses are retried with backoff; API errors raise `ZoraAPIError`.

**Why every field is `Optional`:** the live API leaves out fields its spec marks as required. For example, V4 coins have no `uniswapV3PoolAddress`, and `zoraComments` is absent unless requested. Strict models rejected real responses from 9 of 24 endpoints, so the models accept what the API actually returns. `scripts/validate_live.py` checks every endpoint against production.

## Rewards: what did an address earn?

Every trade on a Zora coin pays out to the coin's creator, the platform that launched it, the interface that routed the trade, and the protocol. On V4 coins those payouts are recorded in `CoinMarketRewardsV4` events, and **none of that event's fields are indexed**. You can't ask a node for "rewards paid to my address"; you have to read every reward event and filter. `zora_coins.rewards` does that, keeps a local SQLite index, and only fetches blocks it hasn't seen.

```bash
zora-rewards 0xYourAddress --days 30 --html rewards.html
```

Real output for a busy platform-referrer address over the last 6 hours:

```
Zora rewards for 0x55c88bb05602da94fce8feadc1cbebf5b72c2453
291 reward events, blocks 51426603–51437322
  Platform referral          328.5866 ZORA                $2.52  (34 payouts)
  Platform referral       0.000114798 WETH                $0.28  (6 payouts)
  Platform referral            5.4734 USDC                $5.47  (98 payouts)
  Trade referral          1.07698e-07 ETH            $0.0002655  (2 payouts)
  Trade referral             136.7589 ZORA                $1.05  (143 payouts)
  Trade referral          0.000130539 WETH                $0.32  (16 payouts)
  Trade referral             0.737777 USDC                $0.74  (44 payouts)
  Total (current prices)                                     $10.39
```

```python
from zora_coins.rewards import RewardsIndexer, build_report, to_text

with RewardsIndexer("rewards.sqlite", rpc_url="https://mainnet.base.org") as idx:
    idx.scan(["0xYourAddress"], days=30)            # resumable; re-runs fetch only new blocks
    report = build_report(idx.events_for(["0xYourAddress"]), ["0xYourAddress"])
print(to_text(report), report.by_role_usd())
```

- **Roles:** creator (`payoutRecipient`), platform referrer, trade referrer, protocol, and Doppler.
- **Amounts:** kept as exact integers, in both the backing currency (ZORA, ETH, USDC or a creator coin) and the coin itself.
- **USD values:** use **current** token prices from the Zora API, not the price at payout time.
- **Coverage:** legacy V3 coins (`CoinTradeRewards`) with `--v3`.
- **Speed:** on the public Base RPC, six hours of blocks (about 10,800) scans in about 8 seconds. A private RPC (`--rpc`) is faster for long histories.

## Development

```bash
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest                    # offline tests (mocked HTTP and RPC, real recorded payloads)
.venv/bin/python scripts/validate_live.py   # every endpoint against production
./scripts/generate_models.sh        # refresh models from Zora's OpenAPI spec
```

## License

MIT © Rebel Studios Software
