auradefi in five lines, then the whole library in one file.
Run it
pip install auradefi python examples/quickstart.py
No keys. No network. No configuration. Auradefi.sandbox() replays a
recording bundled inside the package, and every layer above the transport is
the production one: the same source, decoder, ledger and pricing a live
instance uses. Sandbox data is a RECORDING, so the numbers here are
constants, which is what makes them safe to assert.
This file is also the smoke test CI, scripts/release_check.sh (against a
freshly built wheel in a clean venv) and docker run --network none all
execute, so nothing in it may depend on the repository.
Each section maps to one SPEC phase, and to a guide that goes deeper:
the five lines examples/01_holdings_for_an_address.py
0 money, chains, assets, ledger docs/books/01_foundation … 04_ledger
1 balances -> holdings examples/01, examples/04
2 tenancy and the token mint examples/06
3 transaction decode and reorg examples/04
4 DeFi positions examples/07
5 embedding in your backend examples/02, examples/03
6 Bitcoin xpub derivation examples/10
7 Solana Token-2022 examples/10
8 webhook signing examples/09
9 cost basis and PnL examples/08
What it prints
auradefi 0.1.1: sandbox quickstart, no keys
--- a priced portfolio, in five lines -----------------------------
ETH 2 @ 2500 USD = 5000.000000000000000000 USD
USDC 25 @ 1 USD = 25.000000 USD
total 5025.000000000000000000 USD
^ that is the entire program. Everything below is detail.
--- phase 0: money is exact, and a raw amount is a string ---------
10^77 at 18 decimals -> 10000000000000000000… (60 digits, exact)
wire form: {"raw": "4878123456789012345678", "decimals": 18, "numeric": "4878.123456789012345678", "float": 4878.123456789012}
5 chains seeded; CAIP-19 canonicalised: eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
--- phase 1: balances + prices -> holdings, exactly ---------------
ETH 2 @ 2500 USD = 5000.000000000000000000 USD
USDC 25 @ 1 USD = 25.000000 USD
total 5025.000000000000000000 USD (exact Decimal, never a float)
on the wire: {"amount": "5025.000000000000000000", "currency": "USD"}
an unrecorded request is refused: sandbox cannot reach the network
--- phase 2: two tenants, and one cannot see the other ------------
A's token under B's secret: AuthError: token failed authentication
minted adk_live_09e9… -> user token for host-user-1, scopes ('accounts:read',), ttl 600s
--- phase 3: decode -> parts/fees, bridge -> ledger, reorg --------
txn_f7e3f7aba9d6775a: type=receive parts=1 fees=1 (fee borne_by=counterparty)
reorg at block 101 -> added txn_e5e72767… (block 105); cursor 00000000000000000002 -> 00000000000000000003
--- phase 4: positions drill down, and the projection invariant ---
gross 35841.70000000000000000000 USD − debt 4999.195000000000 USD = net 30842.50500000000000000000 USD
synthetic holdings: 10 ETH, -5000 USDC
a Plaid-only client summing institution_value gets 30842.50500000000000000000: exactly the net worth
--- phase 5: embedding: your ports, your tick, your database ------
sync: 5 pages, 7 transactions across 1 connection(s)
immediate re-sync: no_op=True, zero requests
one connection's failure lands in report.failed_connections, never
in a lost tick: branch on it every time (examples/02)
26 scalar metrics: portfolio_value_usd=5025.0, transaction_count=7.0
--- phase 6+7: Bitcoin derives locally; Solana can break raw/10^d -
BIP32 derived in-process: the extended key never goes near HTTP:
m/0/0 bc1qp5wfcq48h6d63wyy9qz0awtpfqwwv4sma86mhz
m/0/1 bc1qrfxr69jqnhwufxgkqgcdep9prq4j4vuw2wyg0v
m/0/2 bc1qhvd6suvqzjcu9pxjhrwhtrlj85ny3n2mqql5w4
Token-2022 ScaledUiAmount: raw/10^decimals = 1, node says 2, scaled_ui=True: both carried
--- phase 8: webhooks are signed, and verification is shipped -----
tampered body rejected: webhook signature verification failed
X-Auradefi-Signature: v1=1dd3fd7557f27dfcd8c3389c099f4a5…
--- phase 9: four costing methods, four legal answers -------------
bought at 10, 30, 26; sold one unit for 40:
fifo realised 30 USD
lifo realised 14 USD
hifo realised 10 USD
acb realised 18 USD
1 ms before the sale: realised 0 USD, 3 open lots: any instant is answerable, nothing is pre-computed
--- optional extras (installed only with [sql] / [api]) -----------
[sql] SqlModelLedger round-tripped 2 rows through the host's sqlite
[api] GET /coverage: 5 chains, eip155:1 -> ['balances', 'prices', 'transactions']
quickstart OK: nothing above touched the network
The whole file
"""auradefi in five lines, then the whole library in one file. pip install auradefi python examples/quickstart.py No keys. No network. No configuration. `Auradefi.sandbox()` replays a recording bundled inside the package, and every layer above the transport is the production one: the same source, decoder, ledger and pricing a live instance uses. Sandbox data is a RECORDING, so the numbers here are constants, which is what makes them safe to assert. This file is also the smoke test CI, `scripts/release_check.sh` (against a freshly built wheel in a clean venv) and `docker run --network none` all execute, so nothing in it may depend on the repository. Each section maps to one SPEC phase, and to a guide that goes deeper: the five lines examples/01_holdings_for_an_address.py 0 money, chains, assets, ledger docs/books/01_foundation … 04_ledger 1 balances -> holdings examples/01, examples/04 2 tenancy and the token mint examples/06 3 transaction decode and reorg examples/04 4 DeFi positions examples/07 5 embedding in your backend examples/02, examples/03 6 Bitcoin xpub derivation examples/10 7 Solana Token-2022 examples/10 8 webhook signing examples/09 9 cost basis and PnL examples/08 """ from __future__ import annotations import json from decimal import Decimal import auradefi print(f"auradefi {auradefi.__version__}: sandbox quickstart, no keys\n") def section(title: str) -> None: print(f"\n--- {title} " + "-" * max(0, 62 - len(title))) # ===================================================== the whole ask, first section("a priced portfolio, in five lines") from auradefi import Auradefi aura = Auradefi.sandbox() for holding in aura.holdings()[0].holdings: print(f" {holding.symbol:>5} {str(holding.quantity):>4} @ {holding.price}" f" = {holding.value}") (sandbox_report,) = aura.holdings() assert str(sandbox_report.total_value) == "5025.000000000000000000 USD" print(f" total {sandbox_report.total_value}") print(" ^ that is the entire program. Everything below is detail.") # --------------------------------------------------------------- phase 0 section("phase 0: money is exact, and a raw amount is a string") from auradefi.money.decimal_json import quantity_to_wire from auradefi.money.fiat import Money from auradefi.money.quantity import Quantity huge = Quantity(10**77, 18) assert str(huge) == "1" + "0" * 59 # exact, never scientific notation wire = quantity_to_wire(Quantity(4878123456789012345678, 18)) assert isinstance(wire["raw"], str), "rule #2: raw is never a JSON number" assert wire["numeric"] == "4878.123456789012345678" print(f"10^77 at 18 decimals -> {str(huge)[:20]}… ({len(str(huge))} digits, exact)") print(f"wire form: {json.dumps(wire)}") from auradefi.assets.caip import canonical_caip19, parse_caip19 from auradefi.chains.registry import ChainRegistry chains = ChainRegistry() assert [chain.caip2 for chain in chains.chains()][:2] == [ "bip122:000000000019d6689c085ae165831e93", "eip155:1", ] mixed = "eip155:1/erc20:0xA0b86991c6218b36c1D19D4a2e9Eb0cE3606eB48" assert canonical_caip19(mixed) == mixed.lower() assert parse_caip19(mixed).namespace == "erc20" print(f"{len(chains.chains())} chains seeded; CAIP-19 canonicalised: {canonical_caip19(mixed)}") # --------------------------------------------------------------- phase 1 section("phase 1: balances + prices -> holdings, exactly") from auradefi.money.decimal_json import money_to_wire from auradefi.money.fiat import Money # The five lines above already did this. What matters is HOW the number is # built: exact `Decimal` throughout, and an asset nobody prices is named in # `report.unpriced` rather than valued at zero. for holding in sandbox_report.holdings: print(f" {holding.symbol:>5} {str(holding.quantity):>4} @ " f"{str(holding.price):>9} = {holding.value}") assert sandbox_report.total_value == Money(Decimal("5025"), "USD") assert sandbox_report.unpriced == () print(f" total {sandbox_report.total_value} (exact Decimal, never a float)") print(f" on the wire: {json.dumps(money_to_wire(sandbox_report.total_value))}") # The offline guarantee is a guarantee: an unrecorded request fails loudly # rather than reaching the network. from auradefi.errors import CassetteMissError from auradefi.sources import sandbox as recording try: recording.client().get("https://api.etherscan.io/v2/api?chainid=999") except CassetteMissError: print(" an unrecorded request is refused: sandbox cannot reach the network") # --------------------------------------------------------------- phase 2 section("phase 2: two tenants, and one cannot see the other") from auradefi.clock import FrozenClock from auradefi.errors import AuthError from auradefi.tenancy.audit import AuditLog from auradefi.tenancy.keys import ApiKeyStore from auradefi.tenancy.models import Environment, Scope from auradefi.tenancy.store import TenancyStore from auradefi.tenancy.tokens import verify_token clock = FrozenClock(1_767_225_600_000) tenancy = TenancyStore() org = tenancy.create_organisation("Acme", clock) project_a = tenancy.create_project(org.id, "tenant-a", Environment.LIVE, clock) project_b = tenancy.create_project(org.id, "tenant-b", Environment.LIVE, clock) key, plaintext = ApiKeyStore().issue( project_a.id, Environment.LIVE, (Scope.USERS_ADMIN,), clock ) assert plaintext.startswith("adk_live_") and len(plaintext) == 57 token = tenancy.mint_user_token( project_a.id, "host-user-1", ["accounts:read"], 600_000, "203.0.113.7", key.id, clock, AuditLog(), ) claims = verify_token(token, signing_secret=project_a.signing_secret, clock=clock) assert claims.project_id == project_a.id try: verify_token(token, signing_secret=project_b.signing_secret, clock=clock) except AuthError as exc: print(f" A's token under B's secret: {type(exc).__name__}: {exc}") print(f" minted {plaintext[:13]}… -> user token for {claims.external_user_id}, " f"scopes {claims.scopes}, ttl {(claims.exp - claims.iat) // 1000}s") # --------------------------------------------------------------- phase 3 section("phase 3: decode -> parts/fees, bridge -> ledger, reorg") from auradefi.decode.pipeline import decode_account from auradefi.ledger.backends.memory import MemoryLedger from auradefi.ledger.bridge import to_ledger_transaction from auradefi.ledger.models import SyncEventKind from auradefi.ledger.reorg import plan_reorg from auradefi.sources.evm.txlist import NormalTxRecord ME = "0x" + "11" * 20 def row(tx_hash: str, block: int, seconds: int) -> NormalTxRecord: return NormalTxRecord( tx_hash=tx_hash, block_number=block, time_stamp=seconds, from_address="0x" + "99" * 20, to_address=ME, value_wei=10**18, gas_used=21_000, gas_price_wei=10**10, is_error=False, ) HASH_A, HASH_B = "0x" + "aa" * 32, "0x" + "bb" * 32 rich = decode_account("eip155:1", "acct_1", ME, [row(HASH_A, 100, 1_700_000_000), row(HASH_B, 101, 1_700_000_100)], []) first = rich[0] assert [part.direction.value for part in first.parts] == ["in"] assert first.fees[0].borne_by.value == "counterparty" # the sender paid the gas assert to_ledger_transaction(first).entries[0].quantity == Quantity(10**18, 18) print(f" {first.id}: type={first.type.value} parts={len(first.parts)} " f"fees={len(first.fees)} (fee borne_by={first.fees[0].borne_by.value})") ledger = MemoryLedger() bridged = [to_ledger_transaction(txn) for txn in rich] ledger.upsert("tenant-a", bridged) page = ledger.sync("tenant-a", None) assert page.next_cursor == "00000000000000000002" and page.has_more is False reorged = to_ledger_transaction(decode_account( "eip155:1", "acct_1", ME, [row(HASH_B, 105, 1_700_000_500)], [] )[0]) events = ledger.apply_reorg("tenant-a", plan_reorg( [ledger.get("tenant-a", txn.id) for txn in bridged], [reorged], from_block=101 )) assert [event.kind for event in events] == [SyncEventKind.ADDED] delta = ledger.sync("tenant-a", page.next_cursor) print(f" reorg at block 101 -> " + ", ".join( f"{event.kind.value} {event.transaction.id[:12]}… (block {event.transaction.block_number})" for event in delta.events ) + f"; cursor {page.next_cursor} -> {delta.next_cursor}") # --------------------------------------------------------------- phase 4 section("phase 4: positions drill down, and the projection invariant") from auradefi.positions.drill import drill, project_to_synthetic_holdings from auradefi.positions.models import ( MetaType, Position, PositionKind, PositionType, ProtocolModule, Underlying, group_id_for, position_id, ) USDC = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" ETH_ID = "eip155:1/slip44:60" USDC_ID = f"eip155:1/erc20:{USDC}" AAVE_POOL = "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2" AWETH = "0x4d5f47fa6a74757f35c14fd3a6ef8e3c9bc514e8" group = group_id_for("aave-v3", "eip155:1", AAVE_POOL) # SPEC §6.3 verbatim: supply 10 ETH, borrow 5,000 USDC. ONE risk unit. positions = [ Position( id=position_id("aave-v3", "eip155:1", AWETH), adapter_id="aave-v3", chain_id="eip155:1", contract_address=AWETH, kind=PositionKind.APP_TOKEN, position_type=PositionType.DEPOSIT, protocol_module=ProtocolModule.LENDING, group_id=group, underlyings=(Underlying(ETH_ID, Quantity(10 * 10**18, 18), MetaType.SUPPLIED),), ), Position( id=position_id("aave-v3", "eip155:1", AAVE_POOL), adapter_id="aave-v3", chain_id="eip155:1", contract_address=AAVE_POOL, kind=PositionKind.CONTRACT_POSITION, position_type=PositionType.LOAN, protocol_module=ProtocolModule.LENDING, group_id=group, underlyings=(Underlying(USDC_ID, Quantity(5000 * 10**6, 6), MetaType.BORROWED),), ), ] prices = {ETH_ID: Money(Decimal("3584.17"), "USD"), USDC_ID: Money(Decimal("0.999839"), "USD")} drilled = drill(positions, prices) synthetic = project_to_synthetic_holdings(drilled) assert drilled.net_worth.amount == Decimal("30842.505") assert {holding.quantity for holding in synthetic} == {Decimal("10"), Decimal("-5000")} naive_sum = sum((holding.institution_value.amount for holding in synthetic), Decimal("0")) assert naive_sum == drilled.net_worth.amount # THE invariant (SPEC §6.3) SYMBOLS = {ETH_ID: "ETH", USDC_ID: "USDC"} print(f" gross {drilled.gross_assets} − debt {drilled.total_debt} = net {drilled.net_worth}") print(" synthetic holdings: " + ", ".join( f"{holding.quantity.normalize():f} {SYMBOLS[holding.asset_id]}" for holding in synthetic)) print(f" a Plaid-only client summing institution_value gets {naive_sum}: exactly the net worth") # --------------------------------------------------------------- phase 5 section("phase 5: embedding: your ports, your tick, your database") # `sandbox()` and `from_env()` differ by one line and nothing else: # # aura = Auradefi.from_env() # your Etherscan key # aura = Auradefi.from_env(ledger=MyLedger()) # + your database # # `sync(budget=N)` caps the source pages ONE call may spend; cursors make # the next call resume; calling it again inside # `settings.sync_min_interval_s` is a no-op that touches no transport. synced = aura.sync(budget=10) assert (synced.pages_fetched, synced.transactions_ingested) == (5, 7) assert aura.sync(budget=10).no_op is True assert synced.failed_connections == () print(f" sync: {synced.pages_fetched} pages, {synced.transactions_ingested} " f"transactions across {len(synced.connections)} connection(s)") print(f" immediate re-sync: no_op=True, zero requests") print(" one connection's failure lands in report.failed_connections, never") print(" in a lost tick: branch on it every time (examples/02)") metrics = {metric.name: metric.value for metric in aura.scalar_metrics()} assert len(metrics) == 26 print(f" 26 scalar metrics: portfolio_value_usd={metrics['portfolio_value_usd']}, " f"transaction_count={metrics['transaction_count']}") # --------------------------------------------------------------- phase 6/7 section("phase 6+7: Bitcoin derives locally; Solana can break raw/10^d") from auradefi.sources.bitcoin.xpub import derive_addresses from auradefi.sources.solana.spl import ( aggregate_by_mint, build_balances, parse_token_accounts, ) XPUB = ( "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoC" "u1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8" ) addresses = derive_addresses(XPUB, "p2wpkh", 0, 0, 3) assert addresses[0] == "bc1qp5wfcq48h6d63wyy9qz0awtpfqwwv4sma86mhz" print(" BIP32 derived in-process: the extended key never goes near HTTP:") for index, address in enumerate(addresses): print(f" m/0/{index} {address}") T22_MINT = "ScaLedUiAmountMint22222222222222222222222222" OWNER = "9wFFyRfZBsuAha4YcuxcXLKwMxJR43S7fPfQLXMFxbAF" # A Token-2022 account whose mint carries a ScaledUiAmount multiplier of 2: # the node's displayed amount is NOT raw / 10**decimals. accounts = parse_token_accounts([{ "pubkey": "T22AcctC3", "account": {"data": {"program": "spl-token-2022", "parsed": {"type": "account", "info": { "mint": T22_MINT, "owner": OWNER, "state": "initialized", "extensions": [{"extension": "scaledUiAmountConfig", "state": {"multiplier": "2"}}], "tokenAmount": {"amount": "1000000000", "decimals": 9, "uiAmount": 2.0, "uiAmountString": "2"}, }}}}, }]) native, scaled = build_balances(3_500_000_000, aggregate_by_mint(accounts)) assert str(native.quantity) == "3.5" assert str(scaled.quantity) == "1" and scaled.ui_amount_string == "2" and scaled.scaled_ui print(f" Token-2022 ScaledUiAmount: raw/10^decimals = {scaled.quantity}, " f"node says {scaled.ui_amount_string}, scaled_ui={scaled.scaled_ui}: both carried") # --------------------------------------------------------------- phase 8 section("phase 8: webhooks are signed, and verification is shipped") from auradefi.webhooks.sign import sign, verify_signature secret = "ab" * 32 body = '{"type":"connection.created","data":{"connection_id":"conn_demo"}}' at_ms = 1_754_000_000_000 signature = sign(secret, at_ms, body) verify_signature(secret, at_ms, body, signature, at_ms) try: verify_signature(secret, at_ms, body + " ", signature, at_ms) except AuthError as exc: print(f" tampered body rejected: {exc}") print(f" X-Auradefi-Signature: {signature[:34]}…") # --------------------------------------------------------------- phase 9 section("phase 9: four costing methods, four legal answers") from auradefi.accounting.lots import AcquisitionEvent, DisposalEvent from auradefi.accounting.pnl import pnl_at DAY = 86_400_000 T0 = 1_700_000_000_000 trades = ( AcquisitionEvent(T0 + 0 * DAY, ETH_ID, Quantity(1, 0), Money(Decimal("10"), "USD"), "txn_b1"), AcquisitionEvent(T0 + 1 * DAY, ETH_ID, Quantity(1, 0), Money(Decimal("30"), "USD"), "txn_b2"), AcquisitionEvent(T0 + 2 * DAY, ETH_ID, Quantity(1, 0), Money(Decimal("26"), "USD"), "txn_b3"), DisposalEvent(T0 + 3 * DAY, ETH_ID, Quantity(1, 0), Money(Decimal("40"), "USD"), "txn_s1"), ) marks = {ETH_ID: Money(Decimal("50"), "USD")} realised = {method: pnl_at(trades, method, T0 + 3 * DAY, marks).realized for method in ("fifo", "lifo", "hifo", "acb")} assert [str(value) for value in realised.values()] == ["30 USD", "14 USD", "10 USD", "18 USD"] print(" bought at 10, 30, 26; sold one unit for 40:") for method, value in realised.items(): print(f" {method:<5} realised {value}") # Arbitrary date: one millisecond earlier, the sale has not happened yet. before = pnl_at(trades, "fifo", T0 + 3 * DAY - 1, marks) assert before.realized == Money(Decimal("0"), "USD") and len(before.open_lots) == 3 print(f" 1 ms before the sale: realised {before.realized}, {len(before.open_lots)} open lots: " "any instant is answerable, nothing is pre-computed") # --------------------------------------------------------- optional extras section("optional extras (installed only with [sql] / [api])") try: from sqlalchemy import create_engine from sqlalchemy.pool import StaticPool from sqlmodel import Session from auradefi.ledger.backends.models import metadata from auradefi.ledger.backends.sqlmodel import SqlModelLedger engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool) metadata.create_all(engine) # the HOST's DDL. The library emits none sql_ledger = SqlModelLedger(session_factory=lambda: Session(engine)) sql_ledger.upsert("tenant-a", bridged) assert len(sql_ledger.sync("tenant-a", None).events) == 2 print(" [sql] SqlModelLedger round-tripped 2 rows through the host's sqlite") except ImportError: print(" [sql] not installed: skipped (pip install 'auradefi[sql]')") try: from fastapi.testclient import TestClient from auradefi.api.app import create_app from auradefi.api.deps import Deps from auradefi.tenancy.quota import QuotaCounter, QuotaLimits from auradefi.tenancy.tokens import RevocationSet from auradefi.webhooks.deliver import WebhookStore api_deps = Deps( tenancy=tenancy, keys=ApiKeyStore(), quota=QuotaCounter(QuotaLimits(1_000, 10_000, 100_000), clock), audit=AuditLog(), revocations=RevocationSet(), ledger=MemoryLedger(), webhooks=WebhookStore(), chains=chains, clock=clock, signing_secret_for={project_a.id: project_a.signing_secret}.get, capabilities={"eip155:1": frozenset({"balances", "transactions", "prices"})}, ) api = TestClient(create_app(api_deps)) coverage = api.get("/coverage").json() ethereum = next(row for row in coverage["chains"] if row["chain_id"] == "eip155:1") assert ethereum["capabilities"]["balances"] is True assert ethereum["capabilities"]["positions"] is False # generated, never prose print(f" [api] GET /coverage: {len(coverage['chains'])} chains, " f"eip155:1 -> {sorted(k for k, v in ethereum['capabilities'].items() if v)}") except ImportError: print(" [api] not installed: skipped (pip install 'auradefi[api]')") print("\nquickstart OK: nothing above touched the network")