You are writing Python against auradefi 0.1.1, an open-source, multi-tenant crypto data aggregator. Its documentation is at https://auradefi.info and its source is at https://github.com/auracarehq/auradefi. Before writing code, read https://auradefi.info/llms.txt, which indexes every page. If you can fetch and hold about 167 KB, read https://auradefi.info/llms-full.txt instead: it is the whole documentation, including every worked example's source. If you can fetch neither, work from the rules below and tell me which parts of your answer you could not check. pip install auradefi # extras: [sql] SQLModel ledger, [api] FastAPI This runs with no credentials, and is where an answer should start: from auradefi import Auradefi aura = Auradefi.sandbox() # no key, no network, no configuration for holding in aura.holdings()[0].holdings: print(holding.symbol, holding.quantity, holding.value) These 13 rules are where auradefi differs from the obvious guess. Most wrong answers about this library break one of them. 1. Start in Sandbox. `Auradefi.sandbox()` needs no key, no network and no configuration: it replays a recording bundled inside the wheel, through the production source, decoder, ledger and pricing code. Going live is one line, `Auradefi.from_env()`. 2. Sandbox answers are constants: one address on `eip155:1`, 2 ETH and 25 USDC worth 5025 USD, seven transactions. Asking it for a different address, chain or page raises `CassetteMissError`, which means the recording does not hold that request. No credential is missing. 3. Amounts are exact. `Quantity` and `Money` wrap `Decimal` and go on the wire as tagged strings, never as JSON numbers. A float anywhere in this arithmetic is a bug. 4. An unpriced asset is never zero. It comes back in `report.holdings` with `price=None` and is named in `report.unpriced`, so a total is either right or visibly incomplete. 5. There are no Bitcoin or Solana prices. The one shipped oracle is DefiLlama: current prices, six EVM chains. Anything else needs your own `prices` port. 6. Chains are CAIP-2 strings. `user.connect_address("eip155:1", "0x…")` works; `"ethereum"` is refused, as is any chain the registry has not been given. 7. Configuration is prefixed. `Settings.from_env()` reads `AURADEFI_ETHERSCAN_API_KEY` and its siblings. A bare `ETHERSCAN_API_KEY` is ignored deliberately, so an unrelated variable cannot become this library's credential. 8. Nothing runs on its own. There is no scheduler, no worker and no background thread. You call `aura.sync(budget=n)` on your own tick, where `budget` caps the source pages that one call may spend; cursors make the next call resume. 9. The default ledger is memory and loses everything at exit. Production means `ledger=SqlModelLedger(session_factory=…)` or your own object. It takes a session factory, not a URL, because your application owns the engine and the migrations. 10. A source is one object satisfying two seams, `balances()` and `fetch_txlist()`. Binding one that has only the first raises at construction time. 11. Ports are structural protocols. There is no base class and no registration: an object with the right methods is the port. Five of them, all optional keyword arguments to `Auradefi.sandbox()` and `Auradefi.from_env()`. 12. Every failure inherits `auradefi.errors.AuradefiError`, so one `except` clause catches this library and nothing else. 13. The gaps are real and documented: no multicall, no on-chain reader for the position adapters, no historical prices, no async surface, no Solana transaction decode. If the reference does not name a symbol, it does not exist. Say so instead of inventing one. Write code that runs. Prefer the shipped defaults, and when I ask for something the package does not do, name the port I bind to get it. If you are unsure whether a symbol exists, fetch its page under https://auradefi.info/reference/ instead of guessing at a signature. My task: