All projects

SentinelFi

Autonomous covenant monitor with CRE consensus, smart metric simulation, and on-chain enforcement.

CRE & AI Risk & Compliance

What it is

Private credit is a $1.7 trillion market where lenders set financial covenants - rules like "leverage must stay below 6.0x" and "DSCR must stay above 1.25x." Today, monitoring these covenants is entirely manual: every quarter, a human analyst reads a PDF financial report, extracts the numbers, checks them against thresholds, and if something is wrong, makes phone calls to trigger a freeze. This process is slow (days of delay), error-prone (single analyst, no verification), and reactive (borrower can default before anyone acts).
SentinelFi eliminates all three failure modes. It is an autonomous pipeline that:

  1. Triggers quarterly via a CRE CronTrigger - no human initiates anything.
  2. Fetches financial reports from an external data source via HTTPClient - each DON node independently.
  3. Extracts metrics via smart simulation proxy - every node calls a Gemini-format API endpoint (our Vercel-hosted proxy) that fetches the financial report server-side and extracts DSCR and leverage via regex. The workflow code is production-ready for real Gemini AI - only the geminiApiUrl config value needs to change.
  4. Achieves BFT consensus - consensusMedianAggregation computes the median across all DON nodes. Even if some nodes are compromised or return wrong values, the median is correct. This transforms independently-extracted data into BFT-verified data.
  5. Enforces on-chain - the consensus values are ABI-encoded and written to LoanFacility.sol via the Keystone Forwarder. The contract checks covenant thresholds and automatically freezes non-compliant loans in the same transaction - zero human delay.
    The system manages a portfolio of multiple loans, each with independent covenant thresholds. A leverage of 5.9x might be healthy for Loan A (max 6.0x) but a breach for Loan B (max 5.0x). The contract handles this correctly per loan.
    The dashboard reads directly from the blockchain - every value displayed (leverage, DSCR, frozen status, event history) comes from actual on-chain getAllLoans() calls and queryFilter() event queries. Nothing in the UI is hardcoded or faked.

How it Works

Smart Contract Layer - contracts/src/LoanFacility.sol

  • Solidity 0.8.24 with OpenZeppelin AccessControl (role-based admin) + Pausable (emergency stop)
  • Receives ABI-encoded (bytes32 loanId, uint256 leverage, uint256 dscr) via onReport() - callable only by the Keystone Forwarder
  • Stores per-loan covenant thresholds AND last reported values on-chain (lastLeverage, lastDscr)
  • Auto-freezes on breach, auto-unfreezes on recovery - emits CovenantBreached, CovenantHealthy, LoanFrozen, LoanUnfrozen events
  • Portfolio view functions: getLoanIds(), getAllLoans(), getLoanHealth()
  • 29 Foundry tests - all passing. Covers: healthy, leverage breach, DSCR breach, both breach, borderline, recovery, access control, pause/unpause, duplicate registration, stored values, multi-loan isolation
  • Built and tested with Foundry v1.6.0, optimizer enabled (200 runs)
    CRE Workflow - cre-workflow/src/workflow.ts
  • TypeScript using @chainlink/cre-sdk, viem, and zod
  • Strictly follows all CRE hard rules: no async/await (.result() only), no Node builtins, no fetch (HTTPClient only), no Buffer (pure-JS base64 encoder), JSON key sorting for deterministic consensus
  • Node Mode: Each DON node independently constructs a Gemini-format API request with sorted JSON keys containing REPORT_URL: in the prompt, calls the smart simulation proxy (which fetches the report server-side and extracts both DSCR and leverage via regex from the document text). In production this would be a real Gemini endpoint; for the demo it's our Vercel-hosted regex proxy.
  • Single consensusMedianAggregation round per loan - both metrics packed into one number via combined encoding (dscr_scaled * 1000000 + leverage_scaled), keeping total HTTP calls to 3 (within CRE's 5-call limit)
  • DON Mode: Scales float values to integers via Math.round() + BigInt(), ABI-encodes with encodeAbiParameters() from viem, writes to chain via EVMClient.writeReport()
  • Config validated at runtime with Zod schema
    Dashboard - frontend/index.html
  • Single-file Bloomberg-terminal-style UI (dark theme, IBM Plex Mono, scanline overlay)
  • Uses ethers.js v6 with a shared provider singleton for direct on-chain reads
  • Multi-loan portfolio table - reads all 3 loans via getAllLoans(), shows live leverage, DSCR, covenant thresholds, frozen status, last update timestamp
  • Real-time metric cards with threshold bars - values come from on-chain state
  • Simulation control panel - sends REAL Ethereum transactions to the contract (not mock UI), shows tx hash and block number
  • CRE pipeline visualization - animated 6-stage diagram showing the actual workflow stages
  • Live event log - queries on-chain events (CovenantBreached, LoanFrozen, etc.) via queryFilter()
  • Polls every 5 seconds
    Simulation Backend - mock-api/
  • server.js - Express.js server with ethers.js v6
    • POST /api/simulate - accepts {loanId, leverage, dscr}, ABI-encodes the payload, and calls onReport() on the deployed contract via the Forwarder key. Returns real tx hash and block number.
    • GET /api/auto-demo - SSE endpoint that runs a scripted 4-step sequence (healthy -> borderline -> breach -> recovery) with real transactions and pipeline animation events
    • GET /api/report - serves mock financial report text (the data source the CRE workflow fetches)
  • api/gemini-proxy.ts - Vercel serverless function that simulates the Gemini API. It speaks the exact Gemini request/response format but extracts metrics via regex instead of LLM inference. Supports combined mode (fetches report server-side, returns both DSCR + leverage) and single mode (legacy fallback). The workflow code is identical whether pointing at this proxy or the real Gemini endpoint.

Links

Created by

  • Arshdeep Singh
  • Parth Singh