Reference · Canonical Vocabulary

risk-graph-indexer Glossary

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.

Jump to: Core terms Node types Node properties Edge types & categories Cypher rules

Core terms

TermDefinition
Risk graphThe property graph in Memgraph that models on-chain risk relationships: addresses as nodes, typed relationships as edges. The product of the whole system.
EntityThe primary node label. Nearly every node is an :Entity; its kind is a property + a secondary label (:Token, :Vault…).
Bare nodeA freshly-created node with only id, graph_id, and pending_enrichment=true. Created by the indexer; filled in later by enrichment.
Monitored setRedis Set monitored:{chainID} of every address the system tracks. Events touching no monitored address are dropped (~95% filter). Grows via bootstrap, promotion, discovery.
Focus tokenA 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.
PromotionAdding a previously-unknown address to the monitored set because it received a focus-token transfer ≥ $1M USD.
DiscoveryEnrichment finding related addresses (proxy impl, multisig owner, curator, deployer) and adding them to the monitored set — the graph's self-expansion mechanism.
graph_idPartition 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).
CursorThe 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 edgeAn 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 edgeA derived risk projection computed by the risk engine, layered under the real topology (e.g. AT_RISK). Hidden from the architecture view.

Node types (19)

From pkg/types/schema.goNodeType constants + NodeTypeToLabel (the category/subcategory value → secondary label).

category valueSecondary labelWhat it is
token:TokenERC-20 / focus token
eoa:EOAExternally-owned account (wallet)
contract:ContractGeneric smart contract
smart-contract-wallet:SmartContractWalletContract-based wallet
multisig:MultisigGnosis-Safe-style multisig
pool:PoolAMM / liquidity pool
exchange-wallet:ExchangeWalletCEX hot/cold wallet
exchange-deposit-address:ExchangeDepositAddressPer-user CEX deposit address
vault:VaultERC-4626 / yield vault
bridge:BridgeCross-chain bridge contract
mev-bot:MevBotMEV searcher/bot
nft:NFTNFT collection
oracle:OraclePrice feed (Chainlink etc.)
protocol:ProtocolProtocol-level grouping
lending_market:LendingMarketMoney market (aToken/cToken/Comet/Morpho market…) — see market_kind
admin:AdminAdmin / privileged role holder
adapter:AdapterAdapter / module contract
project:ProjectInferred project (~30 protocols)
governance_action:GovernanceActionA recorded governance event (target of AFFECTS)

Key node properties

PropertyMeaning · gotchas
idThe node's identity — the Ethereum address. Anchor queries here.
graph_idPartition key — filter on it in every query. A property, not a label; omitting it silently unions partitions.
category / subcategoryCurrent fields for a node's kind. Legacy nodes use type/subtype instead → always read with coalesce(n.type, n.subcategory, n.category).
pending_enrichmenttrue = bare node, metadata not yet trustworthy. Enrichment flips it to false.
symbolToken symbol (filled by enrichment).
usd_priceUSD price (refreshed by the price refresher, sourced from DefiLlama).
market_kindOn lending_market nodes — protocol-specific flavor (aave_v3_atoken, compound_v2_ctoken, morpho_blue_market…). May be NULL on un-migrated rows.
⚠️ Sanity check (CLAUDE.md): a single position worth > $1T USD is a bug (overflow), not a real number. Don't aggregate overflow.

Edge types & categories

From pkg/types/schema.goEdgeType constants + the EdgeCategory map. Direction convention noted where it matters. The type count grows as the system adds protocols; schema.go is canonical.

CategoryEdge typeDirection · meaning
CONTAINSHOLDSwallet → token. Balance on the edge (quantity_raw). The canonical Transfer-derived edge.
POOL_ASSETpool → token. An asset held in an AMM pool.
VAULT_ASSETvault → token. An asset held in a vault.
RESERVE_BACKINGreserve → token. Backing reserves.
CONTROLLED_BYADMIN_CTRLadmin controls a contract.
ADMIN_OFcontract → its admin role.
OWNSowner → owned (e.g. multisig signer).
OWNS_ADMINowns an admin role.
CURATEScurator/manager → vault.
APPROVESowner → spender (ERC-20 approval).
CUSTODY_VIAspoke → hub singleton (Morpho market → Morpho singleton, Uni V4 pool → PoolManager). Excluded from the risk spine walk (super-node).
COLLATERALISED_BYLENDING_COLLATERALloan/market backed by collateral.
VAULT_ALLOCATIONvault → allocation target.
DEPENDS_ONORACLE_DEPthing → price oracle it relies on.
BRIDGE_BACKED_BYwrapper token → lockbox/OFT adapter. Props track lockbox_delta_usd (negative = drained).
DVN_VERIFIESDVN → OFT. A single required DVN = cross-chain single point of failure (rsETH/Kelp exploit shape).
SUBORDINATE_TOjunior token → senior token. First-loss capital (Aave Umbrella stakers haircut before regular holders).
DERIVED_FROMRECEIPT_FORreceipt token → underlying.
DEBT_FORdebt token → underlying.
WRAP_UNWRAPwrapped ↔ unwrapped relationship.
OPERATED_BYDEPLOYED_BYcontract → its deployer.
SERVICE_FORservice/module contract → focus token it serves.
AUDIT_TRAILAFFECTSGovernanceAction → the market entity it mutated.
ANALYTICALAT_RISKfailure point (admin/oracle/vault) → victim focus token. Derived projection, not topology. A token's own-risk = its inbound AT_RISK edges.

Cypher rules (non-negotiable)

From CLAUDE.md § "Graph DB queries — no brute-force" + § "Before writing code".

RuleWhy
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).
Read a node's kind with coalesce(n.type, n.subcategory, n.category). Don't ORDER BY a heterogeneous property like GovernanceAction.value (mixed Int/Float types — undefined ordering).

Cypher write primitives

The verbs the indexer's write path uses (Lesson 4). Memgraph Cypher; see Memgraph docs.

ClauseWhat it doesUsed 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 rowExpand 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.
💡 Idempotency recipe (Lesson 4): coalesce deltas into net state (a pure function of blocks) + write with MERGE + monotonic updated_block guard → re-processing a block yields a byte-identical graph. Always write replay-safe.
⚙️ Atomic-with-cursor: all of a batch's mutations and the 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.