Curated public snapshot of the internal defi-risk-engine monorepo prepared for Uniswap and Balancer grant submissions.
This version preserves the main structure and module README.md files, but only opens the protocol-facing monitoring core that is useful for grant review. The public boundary is documented in PUBLIC_SCOPE.md.
Highlighted public areas:
src/onchain_data/: monitoring engine core, collectors, config handling, explain dispatch logicsrc/strategies/: transaction-driven escalation and risk contribution layersrc/risk_model/: Rust base risk modelsrc/config_builder/: protocol onboarding/config generationdocs/grants/: Uniswap and Balancer grant drafts plus evidence appendix
The original system README from the source repository is preserved below for architectural context.
Technical documentation for the defi-risk-engine repository.
This README.md now serves as the single root-level system document and aggregates information from the existing module-level README.md files without changing those module documents themselves.
defi-risk-engine is a monitoring and risk-explanation platform for DeFi protocols. It consists of several independent runtime services that together do the following:
- collect on-chain and market data for protocols
- calculate base risk and transaction-driven pressure
- build snapshot and chart payloads for the UI
- generate AI explanations for state and events
- publish alerts and realtime websocket updates
- expose dashboard-friendly APIs for the frontend
| Service | Path | Role |
|---|---|---|
| API service | api/ |
Auth, dashboard/bootstrap API, watchlist, alerts, websocket token issuance |
| On-chain engine | src/onchain_data/ |
Main ingestion runtime, snapshots, tx events, charts, audit hydration |
| Explain service | src/explain_service/ |
Durable explain jobs and provider ensemble |
| Alert system | src/alert_system/ |
Router/worker delivery pipeline via Redis Streams |
| Forecast system | src/forecast_system/ |
Chronos-based short-horizon forecasting |
| Risk model | src/risk_model/ |
Rust scoring service for base risk |
| Off-chain news worker | src/offchain_data/ |
RSS/Atom ingestion for protocol_news |
flowchart LR
A["On-chain / Market Sources"] --> B["On-Chain Engine"]
H["RSS / HTML Sources"] --> I["Off-chain News Worker"]
B --> C["MongoDB"]
I --> C
B --> D["Risk Model (Rust)"]
B --> E["Forecast System"]
B --> F["Explain Service"]
B --> G["Alerts WebSocket API"]
F --> C
E --> C
J["Alert Router / Worker"] --> C
J --> K["Telegram / Email / Notification Center"]
L["API Service"] --> C
L --> M["risk-ui"]
M --> L
M --> G
src/onchain_data/index.jsstarts the engine, connects to MongoDB, and syncs the protocol catalog.- Collectors gather TVL, whale, tx behavior, token health, candles, and historical bootstrap data.
- The engine computes derived metrics and calls the Rust risk model.
- The engine obtains forecasts from the forecast service and stores precomputed chart artifacts.
- The engine writes
protocolsnapshots,tx_risk_events,protocolchartpayloads, andmarketcandles. - The engine sends explain requests to
src/explain_service/when needed. - The off-chain news worker writes
protocol_news. - The alert router/worker reads events, routes delivery jobs, and writes user notifications.
api/reads MongoDB and serves user-scoped payloads torisk-ui.- The frontend gets a websocket token through
POST /api/ws/tokenand connects to the alert socket.
src/onchain_data/Main engine runtime.src/explain_service/Explain jobs, provider orchestration, and fusion.src/alert_system/Router/worker alerts pipeline.src/forecast_system/Node orchestration for Chronos forecasting.src/risk_model/Rust scoring service.src/offchain_data/Off-chain protocol news ingestion.
src/db/Mongo connection, schemas, repositories.src/bootstrap/Startup helpers and initial catalog sync.src/strategies/Transaction-driven risk adjustment.src/config_builder/Generator forsrc/onchain_data/config/protocols.json.
api/Authenticated backend for the dashboard.risk-ui/Frontend application.
src/routers/Express-era routers.src/services/Express-era service helpers.src/modules/Shared domain helpers between the legacy layer and the newerapi/src/modules/.
Source: src/onchain_data/README.md
src/onchain_data/ is the central runtime of the entire platform. It is responsible for:
- orchestrating protocol collectors
- persisting snapshots and tx risk events
- triggering explain flows
- hydrating forecast data
- exposing the alerts websocket used by the frontend
src/onchain_data/index.js starts the engine in this order:
- connect to MongoDB
- sync protocol metadata from
config/protocols.json - hydrate stored ABI and contract-audit intelligence
- start the alerts websocket API
- bootstrap missing snapshots
- bootstrap scheduled explain requests
- start transaction monitoring
- start the model explanation loop
- start periodic collector loops
- asynchronously refresh contract audits
The on-chain engine is orchestrated by src/onchain_data/index.js.
Startup stack:
loadProtocolsConfig
-> buildProtocolRuntimeEntries
-> main
-> MongoConnection.connect
-> syncRuntimeIndexes
-> initProtocolsFromConfig
-> hydrateProtocolEntriesWithAbi
-> hydrateProtocolEntriesWithStoredAudits
-> alertsApi.start
-> bootstrapMissingSnapshots
-> startExplainRequestScheduler
-> startProtocolRiskExplanationLoop
-> startCollectorsLoop
-> bootstrapProtocolContractAudits
Collector tick stack:
startCollectorsLoop.tick
-> runWithConcurrency(protocolRuntimeEntries)
-> runCollectorsForProtocol(entry)
-> collectSnapshot
-> setupCollectors
-> runCollectorWithSharedCache
-> ProtocolSnapshotBuilder.build
-> hydrateCollectorsFromLatestSnapshot
-> calculateDerived
-> runInference
-> buildCombinedRisk
-> requestForecast
-> ProtocolSnapshotRepository.save
-> precomputeProtocolChartPayload
-> buildRiskAlertPayload / dispatchExplainJob
The execution unit inside index.js is protocol + network, not just protocol.
protocolCatalogEntriesprotocols loaded fromprotocols.jsonprotocolRuntimeEntriesnetwork-expanded runtime scopes produced byresolveProtocolNetworks()- one collector tick
writes one
ProtocolSnapshotand one precomputed chart payload per runtime scope
This is the important split:
- risk, TVL, FDV, derived metrics, and forecast are network-scoped
- price candles remain protocol-global market data
- some legacy collectors remain protocol-global and are reused through the shared collector cache
- the API layer merges global and scoped data differently depending on chart type
- collect protocol state from on-chain and market data sources
- compute derived metrics used by the risk model
- call the Rust risk model service
- request short-horizon forecasts from the forecast service
- persist
protocolsnapshots,tx_risk_events, and chart payloads - dispatch explain requests to
explain-service - publish websocket alert events
src/onchain_data/collectors/TVLCollector,WhaleCollector,TxBehaviourCollector,TokenRiskProfileCollector,CandleCollector,HistoricalBootstrapCollectorsrc/onchain_data/explain/explain rule matching, context construction, dispatchsrc/onchain_data/chartHelpers/precomputed payload generation for dashboard chartssrc/onchain_data/audit/contract capability audit and ABI hydrationsrc/onchain_data/base/collector base abstractions and websocket helpers
Source: src/strategies/README.md
After the base model score, the system adds a transaction-driven contribution.
Formula used by the strategy layer:
R_total = clamp01(R_base + Delta_tx)
Where:
R_basecomes from the Rust risk modelDelta_txis computed from recent transaction events- severity is normalized relative to amount vs. FDV
- time decay and saturation are then applied over a recent window
Illustration:
Source: src/explain_service/README.md
src/explain_service/ does not calculate risk itself. It turns already-normalized risk context into durable explanation jobs:
- accept explain requests
- persist
explain_jobs - run several LLM-backed providers in parallel
- normalize outputs into a fixed-width hypothesis vector
- fuse successful provider outputs
- expose the result through a small HTTP API
- daily protocol refresh explanations
- event-triggered explanations dispatched by the engine
- manual operator or dashboard explain requests
The current implementation uses a practical provider ensemble:
OpenAIProviderClaudeProviderGeminiProvider
RequestDedupercooldown handling, duplicate active jobs, duplicateevent_idExplainJobServicecreates durable explain job payloads and reconstructs context for manual requestsExplainOrchestratorruns enabled providers in parallel and writes job state transitionsHypothesisAggregatorfuses successful provider outputs intojudge_result,final_summary, andconfidence
Source: src/model_explanation/README.md
This is a separate loop from explain_service.
model_explanationexplains the current model state over a time windowexplain_serviceexplains a specific event or trigger through provider fusion
For each protocol it:
- selects a recent time window
- loads recent
ProtocolSnapshot - loads recent
TxRiskEvent - builds a prompt payload
- requests a concise explanation
- persists the result into
protocol_risk_explanations
risk_scoresummarywhy_nowkey_driversconfidencewindow_startwindow_endsnapshot_at
Sources: api/README.md, api/src/modules/README.md, api/src/modules/dashboard/README.md, api/src/modules/alerts/README.md, api/src/modules/ws/README.md
api/ is an authenticated Next.js backend that reads data already persisted by engine-side services and exposes UI-friendly contracts.
It is responsible for:
- user authentication and sessions via NextAuth
- user-scoped dashboard/bootstrap responses
- watchlist and alert subscription management
- query access to alerts, news, model explanations, explain jobs, and contract audits
- short-lived websocket token issuance
- health and development utility endpoints
api/src/app/api/auth/[...nextauth]/route.tsNextAuth entrypointapi/src/app/api/register/route.tscredentials registrationapi/src/app/api/me/*authenticated dashboard-facing data APIsapi/src/app/api/monitor/*watchlist and settings flowsapi/src/app/api/ws/token/route.tswebsocket JWT issuanceapi/src/app/api/telegram/*Telegram account linkingapi/src/app/api/health/*liveness/readiness
dashboardbuilds bootstrap payloads, metrics, precomputed chart payloads, and fallbacksmonitorwatched protocols and alert subscriptionsalertsreadstx_risk_eventsand produces UI-ready alertsnewsreadsprotocol_news, normalizes aliases, and deduplicates articlesmodel-explanationexposes storedprotocol_risk_explanationsexplainlistsexplain_jobsand forwards manual explain requestscontract-auditexposes stored audit artifactssubscriptionresolves plan capabilities and protocol limitswssigns short-lived websocket tokens
| Module | Responsibility | Main Data / Contract |
|---|---|---|
auth |
NextAuth config, providers, JWT/session enrichment | session, JWT, redirect allowlist |
users |
user and Telegram-link persistence models | users, telegram_link_tokens |
userContext |
resolve authenticated Mongo user from session | withUser(...) boundary |
dashboard |
bootstrap payloads, metrics, charts, fallbacks | protocolsnapshots, protocolchartpayloads, marketcandles |
monitor |
watchlist, subscriptions, monitor settings | user_protocols, user_alert_protocols, user_settings |
alerts |
UI-shaped risk event feed and counters | tx_risk_events |
news |
normalized news feed for watchlist and protocol pages | protocol_news |
model-explanation |
recent interval-level model explanations | protocol_risk_explanations |
explain |
list explain jobs and forward manual explain requests | explain_jobs, EXPLAIN_SERVICE_URL |
contract-audit |
expose stored audit findings and capability maps | protocol_contract_audits |
subscription |
plan limits and feature flags | plan to capability mapping |
plans |
minimal shared plan helper | plans.ts |
notifications |
in-app notification persistence model | user_notifications |
protocol |
warm enabled-protocol cache for reads | in-memory protocol cache |
candle_chart |
external price-candle fallback loader | CoinGecko candle contract |
ws |
websocket JWT issuance | short-lived HS256 token |
GET /api/me/bootstrapGET /api/dashboard/bootstrapGET /api/me/dashboard-metrics
GET /api/monitor/protocolsGET /api/monitor/me/protocolsPOST /api/monitor/me/protocolsDELETE /api/monitor/me/protocols/:protocol
GET /api/me/alertsGET /api/me/alerts/countGET /api/me/newsGET /api/me/model-explanationsGET /api/me/explain-jobsPOST /api/me/explain-jobsGET /api/me/contract-audits
POST /api/ws/tokenGET /api/healthGET /api/strategies
Source: src/alert_system/README.md
src/alert_system/ is split into two runtime stages:
- listens to upstream risk and tx event streams
- enriches and filters events
- maps alerts to subscribed users
- enqueues delivery jobs into Redis Streams
- consumes jobs from a Redis Streams consumer group
- delivers alerts to channels
- handles retries and DLQ routing
This removes the for user -> await deliver bottleneck and allows delivery to scale horizontally.
src/alert_system/alertManager.jssrc/alert_system/queue/redisStreamQueue.jssrc/alert_system/deliveryWorker.jssrc/alert_system/index.jssrc/alert_system/worker.jssrc/alert_system/telegramLinkBot.js
Source: src/forecast_system/README.md
src/forecast_system/ is a standalone Node.js service for short-horizon protocol forecasting.
- read protocol snapshots and tx risk events from MongoDB
- build fixed time buckets (
15mor1h) and store them inprotocol_metrics_ts - send metric series to the local Chronos endpoint (Python adapter)
- recompute future risk from forecasted metrics
- store future bars in
predicted_risk_ts
- Node orchestrator:
src/forecast_system - Python predictor adapter:
src/forecast_system/predictor
risk_scoretvl_usdprice_usdfdv_usdalerts_count
Source: src/risk_model/README.md
src/risk_model/ is a Rust scoring service that turns normalized protocol features into a base risk score.
tvltvl_delta_1dtvl_delta_7dprice_delta_1dprice_delta_7dvolume_spikemcap_tvl_ratio
traininferserve
POST /score
Request:
{
"tvl": 1000000,
"tvl_delta_1d": 0.01,
"tvl_delta_7d": -0.03,
"price_delta_1d": 0.02,
"price_delta_7d": -0.09,
"volume_spike": 0.6,
"mcap_tvl_ratio": 0.2
}Response:
{
"risk": 0.42
}Source: src/offchain_data/README.md
This worker ingests RSS/Atom feeds and fallback HTML article pages, matches articles to enabled protocols, and persists them into protocol_news.
- read feeds from
config/protocol_rss_sources.json - fall back to parsing article links from HTML pages when feeds are unavailable
- match each article to enabled protocols from MongoDB
- save matched articles to
protocol_news - deduplicate by
(protocol, dedupeKey)
Source: src/db/README.md
src/db/ is the shared persistence contract between runtime components and the API.
| Collection | Schema / Role |
|---|---|
protocols |
protocol registry and metadata |
protocolsnapshots |
time-series snapshots |
protocolchartpayloads |
precomputed dashboard chart payloads |
marketcandles |
price candles |
tx_risk_events |
normalized transaction risk events |
explain_jobs |
explain-service job state |
protocol_risk_explanations |
model explanation loop output |
users |
user accounts |
user_protocols |
watchlist entries |
user_alert_protocols |
alert subscriptions |
user_settings |
per-user settings |
user_notifications |
in-app notifications |
Mongo.jsProtocolRepository.jsProtocolSnapshotRepository.jsProtocolChartPayloadRepository.jsMarketCandleRepository.jsTxRiskEventRepository.jsExplainJobRepository.jsProtocolRiskExplanationRepository.jsProtocolSnapshotBuilder.js
Source: src/config_builder/README.md
src/config_builder/ generates protocol config for src/onchain_data/config/protocols.json.
contractsflaggedMethodsadminMethodsprotocolContractswhalesownerswhaleTransferMinliquidityShockAmounttvlwhaletoken_health
- ABI/source: Etherscan-like API, fallback Blockscout
- TVL: DefiLlama
- whales: Ethplorer / CoinGecko / Moralis
- thresholds: CoinGecko + DexScreener liquidity
Sources: src/bootstrap/README.md, src/modules/README.md, src/services/README.md, src/routers/README.md
src/bootstrap/ contains startup helpers for the initial persistent state before long-running loops begin.
Current main scope:
src/bootstrap/initProtocols.jsupserts protocol metadata fromsrc/onchain_data/config/protocols.jsoninto theProtocolcollection
src/modules/ contains small shared domain modules:
monitor/plans/userContext/
src/services/legacy Express-era service helperssrc/routers/earlier Express router layer withmonitorRoutes.js
New product-facing endpoints should go through api/src/app/api/ and api/src/modules/.
The root frontend package in this repository is risk-ui/.
The main runtime contract between risk-ui and the backend looks like this:
- frontend calls
GET /api/me/bootstrap - focused reads go through
alerts,news,dashboard-metrics,model-explanations, andcontract-audits - realtime auth goes through
POST /api/ws/token - websocket connection is established against the engine-side alerts socket
Although risk-ui currently does not have a dedicated root README, the project structure and deployment docs make the frontend scope clear:
- App Router application with authenticated dashboard flows
- protocol selector, metrics cards, charting, alerts, reasoning, audit, and news views
- consumption of
api/bootstrap endpoints and engine websocket updates - deployment via
Dockerfile.risk-uianddeploy/k8s/risk-ui
Source: deploy/k8s/risk-ui/README.md
- image builds from
Dockerfile.risk-ui - public runtime config is passed through
NEXT_PUBLIC_API_BASE_URLandNEXT_PUBLIC_ALERTS_WS_URL - ingress is configured for the application domain in
deploy/k8s/risk-ui/ingress.yaml
Sources: api/README.md, api/src/modules/ws/README.md, src/onchain_data/README.md
- frontend calls
POST /api/ws/token - API signs a short-lived HS256 JWT
- frontend connects to the alerts websocket with
?token=... - the engine-side websocket server validates the token and streams updates
Source: deploy/k8s/README.md
Kubernetes manifests are split by service:
deploy/k8s/alert-systemdeploy/k8s/risk-modeldeploy/k8s/onchain-enginedeploy/k8s/offchain-newsdeploy/k8s/chronosdeploy/k8s/forecast-servicedeploy/k8s/apideploy/k8s/risk-ui
Deploy example:
kubectl apply -k deploy/k8s/risk-model
kubectl apply -k deploy/k8s/chronos
kubectl apply -k deploy/k8s/forecast-service
kubectl apply -k deploy/k8s/onchain-engine
kubectl apply -k deploy/k8s/offchain-news
kubectl apply -k deploy/k8s/alert-system
kubectl apply -k deploy/k8s/api
kubectl apply -k deploy/k8s/risk-uiThe module-level documentation implies the following important operational realities:
- if MongoDB is unavailable, the engine will not start
- if the risk model is unavailable, snapshots may still be collected, but scoring degrades
- if the forecast service is unavailable, snapshot persistence may continue without enriched future bars
- external market providers may return partial collector results
- explain dispatch and contract audit are best-effort side flows
- alert delivery operates with at-least-once semantics
Below is the list of source README files from which this master document was assembled.
README.mdsrc/onchain_data/README.mdsrc/explain_service/README.mdsrc/alert_system/README.mdsrc/model_explanation/README.mdsrc/offchain_data/README.mdsrc/forecast_system/README.mdsrc/forecast_system/predictor/README.mdsrc/risk_model/README.md
src/db/README.mdsrc/bootstrap/README.mdsrc/config_builder/README.mdsrc/strategies/README.mdsrc/modules/README.mdsrc/services/README.mdsrc/routers/README.mdsrc/onchain_data/audit/README.md
api/README.mdapi/src/modules/README.mdapi/src/modules/dashboard/README.mdapi/src/modules/alerts/README.mdapi/src/modules/auth/README.mdapi/src/modules/users/README.mdapi/src/modules/ws/README.mdapi/src/modules/model-explanation/README.mdapi/src/modules/notifications/README.mdapi/src/modules/candle_chart/README.mdapi/src/modules/explain/README.mdapi/src/modules/monitor/README.mdapi/src/modules/news/README.mdapi/src/modules/contract-audit/README.mdapi/src/modules/subscription/README.mdapi/src/modules/plans/README.mdapi/src/modules/userContext/README.mdapi/src/modules/protocol/README.md
deploy/k8s/README.mddeploy/k8s/onchain-engine/README.mddeploy/k8s/risk-ui/README.mddeploy/k8s/risk-model/README.mddeploy/k8s/api/README.mddeploy/k8s/alert-system/README.mddeploy/k8s/chronos/README.mddeploy/k8s/offchain-news/README.mddeploy/k8s/forecast-service/README.mddeploy/k8s/explain-service/README.mdscripts/sandbox/README.md
The repository also contains vendor README.md files inside bundled font assets:
risk-ui/fonts/Archivo_Complete/Fonts/WEB/README.mdrisk-ui/fonts/Roundo_Complete/Fonts/WEB/README.mdrisk-ui/fonts/Satoshi_Complete/Fonts/WEB/README.md
They relate to bundled font assets rather than the product runtime architecture, so they are not broken out as primary modules in this technical overview.
Treat this file as the root technical map of the system, and use the local README.md files inside modules as the more detailed source of truth for a specific runtime or service boundary.



