Metadata-Version: 2.4
Name: auradefi
Version: 0.1.0
Summary: Open-source multi-tenant crypto data aggregator: Vezgo-style tenancy, DeBank-style DeFi depth, Plaid wire format. Library first, service second.
Project-URL: Homepage, https://github.com/auracarehq/auradefi
Project-URL: Source, https://github.com/auracarehq/auradefi
Project-URL: Issues, https://github.com/auracarehq/auradefi/issues
Author-email: Auracare <stephen@auracare.org.uk>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: aggregator,blockchain,caip,caip-19,crypto,defi,multi-tenant,plaid,portfolio,wallet
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Provides-Extra: api
Requires-Dist: fastapi>=0.115; extra == 'api'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: fastapi>=0.115; extra == 'dev'
Requires-Dist: ipykernel; extra == 'dev'
Requires-Dist: nbclient; extra == 'dev'
Requires-Dist: nbformat; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: sqlmodel>=0.0.16; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: sql
Requires-Dist: sqlmodel>=0.0.16; extra == 'sql'
Description-Content-Type: text/markdown

# auradefi

Open-source, multi-tenant crypto data aggregator. The tenancy model of
Vezgo, the DeFi position depth of DeBank, the transaction decomposition of
Zerion, and **Plaid's wire format** — so crypto merges with bank and
exchange data in one schema downstream.

**Library first, service second.** A Python host imports `auradefi`
directly and pays no serialisation or network cost; the HTTP API is a thin
shell over the importable core.

> Status: alpha. All ten SPEC phases are implemented — **3,027 tests, green
> on a fresh clone with no API keys and no network**. The capability table
> below says exactly what that does and does not mean;
> [`STATUS.md`](STATUS.md) records the phase gates and known caveats, and
> [`docs/SPEC.md`](docs/SPEC.md) is the full design contract.

## Install

```bash
pip install auradefi                # core — httpx is the only dependency
pip install 'auradefi[sql]'         # + the SQLModel ledger backend
pip install 'auradefi[api]'         # + the FastAPI HTTP surface
```

From a clone (no pip on your system python? `scripts/bootstrap.sh` handles it):

```bash
git clone https://github.com/auracarehq/auradefi
cd auradefi && bash scripts/bootstrap.sh
.venv/bin/pytest                              # 3,027 tests, offline, no keys
.venv/bin/python docs/examples/quickstart.py  # every phase, end to end
```

## Using it

**As a library** — the host owns storage, transport, prices and the tick
(SPEC §8):

```python
from auradefi import Auradefi

auradefi = Auradefi(
    ledger=SqlModelLedger(session_factory=my_session_factory),  # your database
    source=MySource(),        # your transport: .balances() + .fetch_txlist()
    prices=MyPrices(),        # your price feed: .usd_prices()
)
user = auradefi.user("opaque-host-user-id")   # get-or-create, id is derived
user.connect_address("eip155:1", "0x…")       # validated now, not on a later tick
report = auradefi.sync(budget=5)              # budgeted, resumable, self-throttling
holdings, metrics = auradefi.holdings(), auradefi.scalar_metrics()
```

**As a service** — `create_app` takes ports you already built:

```python
from auradefi.api.app import create_app
from auradefi.api.deps import Deps

app = create_app(Deps(tenancy=…, keys=…, ledger=…, webhooks=…, clock=…))
# POST /auth/token   ·  POST /connections  ·  GET /crypto/sync  (Plaid's envelope)
# GET  /coverage     ·  POST /webhooks/endpoints  ·  POST /webhooks/…/replay
```

Both surfaces are walked — executably, offline — in
[`docs/books/`](docs/books).

## What works today

Coverage published as data, not prose optimism (rule #10). Every row marked
**works** has an executable notebook under [`docs/books/`](docs/books) that
runs offline and asserts its own outputs, plus a gate test under `tests/`.

| Capability | Phase | Status |
|---|---|---|
| `Quantity`/`Money`: exact at 10^77, four-field wire form, `raw` always a JSON string, strict wire grammar | 0 | **works** — [`02_money`](docs/books/02_money.ipynb) |
| CAIP-2/CAIP-19 parse + canonicalize, deterministic `ast_…` ids, both-ways asset registry | 0 | **works** — 5 seed chains (Ethereum, Polygon, Base, Bitcoin, Solana); [`03_assets_chains`](docs/books/03_assets_chains.ipynb) |
| Asset groups (decimals-equality law, `single` fallback) + additive spam scoring (score + numbers, caller threshold) | 0 | **works** — [`03_assets_chains`](docs/books/03_assets_chains.ipynb) |
| Ledger port: idempotent upsert, cursor sync with `has_more` paging, reorg = `removed` + re-`added`, resurrection, tenant isolation | 0 | **works** — memory **and** SQLModel backends; [`04_ledger`](docs/books/04_ledger.ipynb) |
| Cassette replay harness (`CassetteMissError` offline guarantee) | 0 | **works** — [`01_foundation`](docs/books/01_foundation.ipynb) |
| Style gates: size, structure, placement, layering (`tests/style`) | 0 | **works** — no allowlist |
| EVM balances → holdings, exact-`Decimal` USD totals, unpriced assets named not guessed | 1 | **works** — Etherscan V2 source + DefiLlama prices; [`05_holdings`](docs/books/05_holdings.ipynb) |
| Tenancy: org/project/end-user, scoped `adk_` keys, `authEndpoint` JWT mint, three-window quota, audit log | 2 | **works** — isolation gate actively tries to leak; [`06_tenancy`](docs/books/06_tenancy.ipynb) |
| Rich transactions: `parts[]`/`acts[]`, fees as siblings carrying `borne_by`, derived `type`, ledger bridge, reorg + resurrection | 3 | **works** — EVM only, one act per transaction; [`07_transactions`](docs/books/07_transactions.ipynb) |
| DeFi positions: adapter protocol, drill-down, group totals + health factor, signed synthetic-Holdings projection | 4 | **works** — Uniswap v2/v3, Aave v3, Lido/Rocket Pool; **fixture-driven, see below**; [`08_positions`](docs/books/08_positions.ipynb) |
| Embedding: `from auradefi import Auradefi`, host-owned session, budgeted two-phase sync, 26-metric scalar projection | 5 | **works** — second sync is a no-op proven by counting requests; [`09_embedding`](docs/books/09_embedding.ipynb) |
| Bitcoin: pure-Python BIP32 xpub derivation, gap-20 scan, confirmed-only UTXO balances | 6 | **works** — p2wpkh + Esplora only; the extended key never reaches HTTP; [`10_bitcoin_solana`](docs/books/10_bitcoin_solana.ipynb) |
| Solana: SPL + Token-2022 balances, ScaledUiAmount carried both ways, signature history | 7 | **works** — balances only, no decode; [`10_bitcoin_solana`](docs/books/10_bitcoin_solana.ipynb) |
| HTTP API: Plaid `/crypto/sync` envelope, connections, `/coverage` generated as data, nine quota headers, batch holdings | 8 | **works** — [`12_http_api`](docs/books/12_http_api.ipynb) |
| Webhooks: HMAC-SHA256 signed, durable over a pinned retry schedule, dead letter + replay | 8 | **works** — [`12_http_api`](docs/books/12_http_api.ipynb) |
| Accounting: lot ledger, FIFO/LIFO/HIFO/ACB, realised + unrealised PnL, **arbitrary-date** PnL, Plaid `tax_lots` | 9 | **works** — 50,000-event gate; [`11_accounting`](docs/books/11_accounting.ipynb) |

### What is **not** there

Stated plainly, because rule #10 cuts both ways.

- **No live network adapters beyond what the cassettes cover.** Every I/O
  path is exercised against committed recordings. Pointing a source at the
  real Etherscan / Esplora / Solana RPC needs your own keys and endpoints,
  and has not been reconciled against an incumbent in CI.
- **Positions are fixture-driven.** The `ContractReader` seam ships and
  every adapter is pinned to block-20,450,000 golden vectors, but **no
  concrete on-chain reader ships** — there is no `eth_call` transport and no
  multicall batcher in the package. A host must supply its own reader to run
  the adapters against a live chain.
- **No multicall anywhere.** Token balances cost one request each.
- **One price oracle** (DefiLlama), current prices only. No fallback feed and
  no historical price service, so accounting marks are the caller's.
- **Two ledger backends**: in-memory and SQLModel/sqlite. Postgres should
  work through the same port; only sqlite is exercised. Tenancy, keys,
  quota, audit and webhook stores are **in-memory only**.
- **Cosmos is absent**, as is every EVM chain the registry does not seed,
  along with exchange connections, NFTs and protocol-specific decoders
  (`acts[]` is always one act and `protocol` is always `None`).
- **Solana transaction decode is not implemented** — balances and signature
  history only.
- No async surface, no background worker, no scheduler: the host owns the
  tick.

## The rules the code lives by

- Money is a tagged decimal string; a raw amount is **never** a JSON integer.
- Asset ids are deterministic CAIP-19 and permanently stable.
- Every movement is a `part[]`; fees are siblings, never movements.
- Multi-tenancy is designed in; two tenants can never see each other's data.
- `pytest` passes on a fresh clone with **no API keys** — cassettes committed.
- Files cap at 400 lines with no allowlist; the layer contract is enforced
  by tests (`tests/style/`), not by convention.

## Docker

```bash
docker compose run --rm test    # full offline suite in a network-less container
docker compose run --rm demo    # quickstart against the installed wheel
```

## Docs

- [`docs/SPEC.md`](docs/SPEC.md) — the design contract
- [`docs/books/`](docs/books) — twelve executable notebooks, run headlessly in
  CI so they cannot rot
- [`docs/DECISIONS.md`](docs/DECISIONS.md) — every pinned algorithm and id formula
- [`STATUS.md`](STATUS.md) — phase gates and known caveats
- [`docs/AGENT_PROMPTS.md`](docs/AGENT_PROMPTS.md) — the agent loop that builds this repo, with copy-paste prompts
- [`docs/RELEASING.md`](docs/RELEASING.md) — pip + Docker release procedure

## Licence

Apache-2.0 — see [`LICENSE`](LICENSE).
