The ground-truth nomenclature for the graph and pipeline. Future lessons adhere to these terms.
Source of truth: pkg/types/schema.go + docs/architecture.md + CLAUDE.md.
| Term | Definition |
|---|---|
| Risk graph | The property graph in Memgraph that models on-chain risk relationships: addresses as nodes, typed relationships as edges. The product of the whole system. |
| Entity | The primary node label. Nearly every node is an :Entity; its kind is a property + a secondary label (:Token, :Vault…). |
| Bare node | A freshly-created node with only id, graph_id, and pending_enrichment=true. Created by the indexer; filled in later by enrichment. |
| Monitored set | Redis Set monitored:{chainID} of every address the system tracks. Events touching no monitored address are dropped (~95% filter). Grows via bootstrap, promotion, discovery. |
| Focus token | A token the system specifically tracks (with USD-per-raw-unit multipliers from metadata.token_runs). A transfer of one ≥ $1M can promote a new address into the monitored set. |
| Promotion | Adding a previously-unknown address to the monitored set because it received a focus-token transfer ≥ $1M USD. |
| Discovery | Enrichment finding related addresses (proxy impl, multisig owner, curator, deployer) and adding them to the monitored set — the graph's self-expansion mechanism. |
| graph_id | Partition key. Multiple independent graphs coexist in one Memgraph instance, separated only by this property on every node and edge (e.g. risk-graph-rt, test_carlos). |
| Cursor | The last block the indexer committed, stored as a BlockCursor in Memgraph — written atomically with the block's mutations so it can always resume exactly. |
| Architectural edge | An edge describing real on-chain topology (who holds/controls/backs what). Categories: CONTAINS, CONTROLLED_BY, COLLATERALISED_BY, DEPENDS_ON, DERIVED_FROM, OPERATED_BY, AUDIT_TRAIL. |
| Analytical edge | A derived risk projection computed by the risk engine, layered under the real topology (e.g. AT_RISK). Hidden from the architecture view. |
From pkg/types/schema.go — NodeType constants + NodeTypeToLabel (the category/subcategory value → secondary label).
| category value | Secondary label | What it is |
|---|---|---|
token | :Token | ERC-20 / focus token |
eoa | :EOA | Externally-owned account (wallet) |
contract | :Contract | Generic smart contract |
smart-contract-wallet | :SmartContractWallet | Contract-based wallet |
multisig | :Multisig | Gnosis-Safe-style multisig |
pool | :Pool | AMM / liquidity pool |
exchange-wallet | :ExchangeWallet | CEX hot/cold wallet |
exchange-deposit-address | :ExchangeDepositAddress | Per-user CEX deposit address |
vault | :Vault | ERC-4626 / yield vault |
bridge | :Bridge | Cross-chain bridge contract |
mev-bot | :MevBot | MEV searcher/bot |
nft | :NFT | NFT collection |
oracle | :Oracle | Price feed (Chainlink etc.) |
protocol | :Protocol | Protocol-level grouping |
lending_market | :LendingMarket | Money market (aToken/cToken/Comet/Morpho market…) — see market_kind |
admin | :Admin | Admin / privileged role holder |
adapter | :Adapter | Adapter / module contract |
project | :Project | Inferred project (~30 protocols) |
governance_action | :GovernanceAction | A recorded governance event (target of AFFECTS) |
| Property | Meaning · gotchas |
|---|---|
id | The node's identity — the Ethereum address. Anchor queries here. |
graph_id | Partition key — filter on it in every query. A property, not a label; omitting it silently unions partitions. |
category / subcategory | Current fields for a node's kind. Legacy nodes use type/subtype instead → always read with coalesce(n.type, n.subcategory, n.category). |
pending_enrichment | true = bare node, metadata not yet trustworthy. Enrichment flips it to false. |
symbol | Token symbol (filled by enrichment). |
usd_price | USD price (refreshed by the price refresher, sourced from DefiLlama). |
market_kind | On lending_market nodes — protocol-specific flavor (aave_v3_atoken, compound_v2_ctoken, morpho_blue_market…). May be NULL on un-migrated rows. |
From pkg/types/schema.go — EdgeType constants + the EdgeCategory map. Direction convention noted where it matters. The type count grows as the system adds protocols; schema.go is canonical.
| Category | Edge type | Direction · meaning |
|---|---|---|
| CONTAINS | HOLDS | wallet → token. Balance on the edge (quantity_raw). The canonical Transfer-derived edge. |
POOL_ASSET | pool → token. An asset held in an AMM pool. | |
VAULT_ASSET | vault → token. An asset held in a vault. | |
RESERVE_BACKING | reserve → token. Backing reserves. | |
| CONTROLLED_BY | ADMIN_CTRL | admin controls a contract. |
ADMIN_OF | contract → its admin role. | |
OWNS | owner → owned (e.g. multisig signer). | |
OWNS_ADMIN | owns an admin role. | |
CURATES | curator/manager → vault. | |
APPROVES | owner → spender (ERC-20 approval). | |
CUSTODY_VIA | spoke → hub singleton (Morpho market → Morpho singleton, Uni V4 pool → PoolManager). Excluded from the risk spine walk (super-node). | |
| COLLATERALISED_BY | LENDING_COLLATERAL | loan/market backed by collateral. |
VAULT_ALLOCATION | vault → allocation target. | |
| DEPENDS_ON | ORACLE_DEP | thing → price oracle it relies on. |
BRIDGE_BACKED_BY | wrapper token → lockbox/OFT adapter. Props track lockbox_delta_usd (negative = drained). | |
DVN_VERIFIES | DVN → OFT. A single required DVN = cross-chain single point of failure (rsETH/Kelp exploit shape). | |
SUBORDINATE_TO | junior token → senior token. First-loss capital (Aave Umbrella stakers haircut before regular holders). | |
| DERIVED_FROM | RECEIPT_FOR | receipt token → underlying. |
DEBT_FOR | debt token → underlying. | |
WRAP_UNWRAP | wrapped ↔ unwrapped relationship. | |
| OPERATED_BY | DEPLOYED_BY | contract → its deployer. |
SERVICE_FOR | service/module contract → focus token it serves. | |
| AUDIT_TRAIL | AFFECTS | GovernanceAction → the market entity it mutated. |
| ANALYTICAL | AT_RISK | failure point (admin/oracle/vault) → victim focus token. Derived projection, not topology. A token's own-risk = its inbound AT_RISK edges. |
From CLAUDE.md § "Graph DB queries — no brute-force" + § "Before writing code".
| Rule | Why |
|---|---|
1. Scope by graph_id on nodes (and often edges). | Multiple partitions share the DB; omitting it unions them silently. |
2. No full-graph scans. Never MATCH (n) with no anchor. Start from a known id or label+index, bound depth, LIMIT. | The graph is huge — an unanchored scan OOMs and times out. |
3. Query both directions. Different seeders may write the same logical edge in opposite directions — check or match -[r]- when unsure. | Never assume an edge's direction. |
| 4. Memgraph has no APOC. Don't reach for APOC procedures — use plain Cypher or Go-side helpers. | The stage DB is Memgraph; APOC is a Neo4j-only fast path (dead code in practice). |
coalesce(n.type, n.subcategory, n.category). Don't ORDER BY a heterogeneous property like GovernanceAction.value (mixed Int/Float types — undefined ordering).The verbs the indexer's write path uses (Lesson 4). Memgraph Cypher; see Memgraph docs.
| Clause | What it does | Used for |
|---|---|---|
MATCH (n {…}) | Find existing nodes/edges. Read-only; never creates. | Reads, and writes that must not create (e.g. only update a token already in the graph). |
CREATE (n) | Unconditionally create. Errors/duplicates if it already exists. | Rare in this codebase — MERGE is preferred for idempotency. |
MERGE (n {…}) | Upsert: match if present, else create. Never duplicates. The workhorse write verb. | Nodes + edges in flush() — safe under replay. |
ON CREATE SET … | Property writes that run only when MERGE created the entity. | Stamping new bare nodes with pending_enrichment=true. |
ON MATCH SET … | Property writes that run only when MERGE matched an existing entity. | Updating without clobbering create-time defaults. |
UNWIND $rows AS row | Expand a list parameter into one iteration per element — the batching primitive. | Writing hundreds of edges in one query (the indexer's hot path). |
WITH … WHERE … | Pipe results forward and filter mid-query. | The monotonic guard: WHERE coalesce(r.updated_block,0) <= row.block drops stale writes. |
MERGE + monotonic updated_block guard → re-processing a block yields a byte-identical graph. Always write replay-safe.SetCursor run in one graphstore.ExecuteWrite transaction — commit together or roll back together.Lessons referencing this glossary: ① Pipeline · ② Data model · ③ Decoder path · ④ Write path. Tip: open this file and ⌘P → "Save as PDF" for a printable cheat-sheet.