Skip to content

Repository files navigation

theorem-plume

RWA data indexer for the Plume Network. Ingests on-chain capital flows and asset valuation from tokenized yield-bearing vaults, exposing them over a typed read API.

Scope

  • Capital flows — async ingestion of ERC-4626 Deposit / Withdraw events from a tokenized RWA vault.
  • Asset valuation — tracks NAVUpdated(uint256,uint256) oracle emissions and computes real-time TVL / market cap in USD.

Stack

  • FastAPI + Pydantic v2
  • SQLModel over PostgreSQL, with an in-memory SQLite fallback for local/test
  • web3.py AsyncWeb3 + AsyncHTTPProvider, fully async ingestion

Plume network posture

Plume is an Arbitrum-Nitro L2 that settles to Ethereum L1. Two properties shape the indexer:

  • Fast blocks. Plume produces blocks far faster than Ethereum's 12s slot, so the ingestion cadence (POLL_INTERVAL_SECS, LOG_CHUNK_SIZE, the readiness lag threshold) is tuned to a sub-second-to-few-second block time rather than inherited Ethereum defaults. Exact values are provisional pending live-RPC confirmation of block time and eth_getLogs range caps.
  • L2 finality. The sequencer gives near-instant soft finality (the latest head), but a block is only reorg-proof once its batch posts and finalizes on L1, which lags by minutes. The indexer reads the finalized tag by default (configurable, plus a confirmation buffer) and persists block hashes per cursor for reorg detection. If an RPC doesn't serve a requested finality tag, head resolution degrades gracefully to latest.

The POA/extraData middleware is enabled by default because Plume blocks carry a non-32-byte extraData that web3.py's default block parsing rejects.

Multi-chain safety

Every persisted row and cursor is stamped with chain_id (Plume mainnet 98866, testnet 98867), folded into the unique constraints and into every NAV/flow lookup. A shared store can hold more than one network without conflating identical (tx_hash, log_index) coordinates or leaking one chain's data into another's valuation. On startup the configured chain_id is asserted against the RPC's reported chain id, so a mismatched RPC_URL / CHAIN_ID pair fails fast rather than silently indexing the wrong network.

Quickstart

git clone <repo> && cd theorem-plume
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,docs]"
cp .env.example .env        # set RPC_URL, CHAIN_ID + contract addresses

Run without a Postgres DSN to fall back to in-memory SQLite.

Layout

src/theorem_plume/
  config.py        settings, dynamic db url resolution
  logging.py       structured json logging
  exceptions.py    error hierarchy
  models/          sqlmodel tables
  chain/           web3 provider, decoders, scrapers
  services/        valuation (read-only, decoupled)
  api/             fastapi routers
  orchestration/   ingestion supervisor + lifespan
tests/

Commands

pytest -q                       # tests
ruff check . && ruff format .   # lint + format
mkdocs serve                    # docs (local)
python scripts/dump_openapi.py  # static openapi schema

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages