Skip to content

Abdr007/flash-risk-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Flash Risk Engine (FRE)

Independent on-chain risk intelligence engine for Flash Trade perpetuals on Solana.

Live Dashboard: dashboard-ebon-sigma-94.vercel.app

Repository: github.com/Abdr007/flash-risk-engine


Overview

Flash Risk Engine reads all public on-chain state from the Flash Trade perpetual exchange program and computes real-time risk metrics for every open position. It operates entirely from publicly available data: program accounts fetched via RPC, oracle prices read from on-chain accounts, and account layouts defined in the published IDL.

The engine does not rely on internal APIs, private data feeds, or proprietary infrastructure. Every input is verifiable on-chain. Every formula references its Rust source.

This is an independent verification tool. It is not affiliated with Flash Trade.

What this engine does

  • Reads all pool, custody, market, position, and oracle accounts from Solana mainnet
  • Computes per-position PnL, margin, leverage, liquidation price, and distance-to-liquidation
  • Detects positions that exceed the maximum leverage threshold (liquidation condition)
  • Models borrow rates using the two-slope kinked utilization model
  • Aggregates protocol-wide risk metrics: liquidation density, leverage distribution, utilization

What this engine does not do

  • Execute transactions or liquidations
  • Access closed-source production logic
  • Predict future price movements
  • Serve as a trading signal

Features

Live leverage computation -- Per-position leverage calculated as size_usd * BPS_POWER / margin_usd, matching the on-chain formula from pool.rs.

Liquidation condition detection -- Identifies positions where current_leverage > max_leverage, the exact condition that triggers the liquidate instruction.

Borrow rate modeling -- Implements the two-slope kinked utilization model from custody.rs, with per-custody base rate, slope1, slope2, and optimal utilization.

Utilization monitoring -- Per-custody utilization computed as locked / owned in native token units, with current rate and distance-to-optimal.

Liquidation density buckets -- Positions bucketed by percentage distance to liquidation: <0.5%, 0.5-1%, 1-2%, 2-5%, 5-10%, >10%.

Leverage distribution histogram -- Open interest bucketed by leverage: 1-2x, 2-5x, 5-8x, 8-12x, 12-20x, 20-50x, 50x+.

Audit mode (--audit) -- Structured verification snapshot outputting raw, scaled, and human-readable values for every computed quantity, with SHA-256 hash of the IDL used.

Post-run sanity checks -- Automated invariant checks: OI summation consistency, bucket totals, utilization cross-validation, borrow rate model verification.

Trace mode (--trace <address>) -- Full diagnostic trace for a single position. Outputs raw decoded fields, oracle values, custody parameters, borrow state, and all computed risk values including data integrity classification.

Data report (--data-report) -- Outputs program ID, RPC endpoint statistics, account decode success rates, failure counts by category, oracle staleness counts, and data integrity classification summary.


Mathematical Model

All formulas below are implemented with arbitrary-precision integer arithmetic (BN.js) to match on-chain Rust behavior. No floating-point is used in intermediate computations. See docs/mathematical-model.md for full derivations and worked examples.

Leverage

leverage = size_usd * 10,000 / margin_usd

Result is in BPS (basis points, 10^4). A leverage of 10x = 100,000 BPS.

Margin

margin_usd = collateral_usd + profit_usd - loss_usd

Clamped to max(0, result). All values in USD_DECIMALS (10^6).

PnL

exit_price = pessimistic_exit(oracle_price, side, spread)

Long:
  profit = size_usd * max(0, exit_price - entry_price) / entry_price
  loss   = size_usd * max(0, entry_price - exit_price) / entry_price

Short:
  profit = size_usd * max(0, entry_price - exit_price) / entry_price
  loss   = size_usd * max(0, exit_price - entry_price) / entry_price

total_loss = price_loss + interest_usd + close_fee_usd
profit = min(profit, locked_value_usd)    // capped at locked collateral

Liquidation

liquidatable = (current_leverage > max_leverage)

Where max_leverage is from custody.pricing.maxLeverage (in BPS).

Liquidation Price

min_margin  = size_usd * BPS_POWER / max_leverage
buffer      = collateral_usd - interest_usd - close_fee_usd - min_margin

Long:   liq_price = entry_price * (size_usd - buffer) / size_usd
Short:  liq_price = entry_price * (size_usd + buffer) / size_usd

Borrow Rate (Two-Slope Kink Model)

if utilization < optimal_utilization:
    rate = base_rate + (utilization / optimal_utilization) * slope1
else:
    rate = base_rate + slope1
           + ((utilization - optimal) / (RATE_POWER - optimal)) * slope2

All parameters in RATE_DECIMALS (10^9). Rates are per-hour. Maximum rate is base_rate + slope1 + slope2.

Interest Accumulation

increment    = ceil(time_delta_seconds * current_rate / 3600)
cumulative  += increment
interest_usd = (cumulative_now - position_snapshot) * locked_usd / RATE_POWER

Ceiling division ensures borrowers are never undercharged.

Unit Summary

Quantity Scale Unit
USD amounts (size, collateral, margin, PnL) 10^6 USD_DECIMALS
Prices (entry, exit, oracle) 10^9 ORACLE_PRICE_SCALE
Leverage, fee ratios 10^4 BPS_DECIMALS
Borrow rates, utilization 10^9 RATE_DECIMALS
Trade spreads 10^4 * 100 100ths of BPS

Data Sources

Source Description
Flash Trade IDL (perpetuals.json) Account layouts, type definitions, discriminators. v15.1.0.
On-chain program accounts Pool, Custody, Market, Position accounts fetched via getProgramAccounts
Custom Oracle accounts Per-custody spot price, EMA, confidence interval, exponent, publish time
PoolConfig.json Mainnet pool addresses (8 pools). Embedded in src/pool-config.ts
Reference implementation flash-perpetuals open-source Rust programs. Formula derivation source
Rust SDK flash-sdk-rust reader functions and state structures

Verified vs Not Verified

Component Status Source
Program ID Verified Cross-checked from IDL, PoolConfig.json, Rust SDK declare_id!
IDL account layouts Verified perpetuals.json v15.1.0
Precision constants (BPS, RATE, USD) Verified Matched between Rust SDK and TS SDK
Oracle price reading Verified Custom oracle with divergence banding, from flash-sdk-rust
PnL computation Verified Matches pool.rs get_pnl_usd
Leverage computation Verified Matches pool.rs get_leverage
Liquidation condition Verified leverage > max_leverage from reference impl
Borrow rate (kink model) Verified Matches custody.rs update_borrow_rate
Lock fee accumulation Verified Ceiling division matches reference impl
Close fee computation Verified size_usd * close_position_fee / RATE_POWER
Liquidation price Partial Derived from liquidation condition; static fee estimate
price_impact_usd handling Not verified Production source closed
degen_size_usd leverage thresholds Not verified Production source closed
unsettled_value_usd settlement Not verified Production source closed
unsettled_fees_usd settlement Not verified Production source closed
Lazer oracle integration Not verified Production source closed

See docs/verification.md for detailed verification methodology.


Usage

Install

npm install

Setup (first time)

Downloads the canonical IDL from the official Flash Trade SDK repository:

npm run setup

Run

# Default (uses public Solana RPC)
npm start

# With custom RPC endpoint
RPC_URL=https://your-rpc-endpoint.com npm start

# With explicit RPC flag
npm start -- --rpc https://your-rpc-endpoint.com

Audit Mode

Outputs a structured verification snapshot with raw, scaled, and human-readable values:

npm start -- --audit

Trace Mode

Full diagnostic trace for a single position (or highest-leverage position if no address given):

# Trace a specific position
npm start -- --trace <positionAddress>

# Trace highest-leverage position (auto-selected)
npm start -- --trace

Data Report

Outputs RPC statistics, decode success rates, and data integrity classification:

npm start -- --data-report

Dashboard

Live: dashboard-ebon-sigma-94.vercel.app

Web-based visualization console (Next.js):

# Generate snapshot data
npm run snapshot

# Start dashboard (development)
cd dashboard && npm run dev

# Start dashboard (production)
cd dashboard && ./scripts/start-production.sh

Open http://localhost:3000. The dashboard reads from dashboard/data/snapshot.json. Re-run npm run snapshot to refresh data.

API endpoints:

  • GET /api/snapshot — Full snapshot data
  • GET /api/trace?position=<address> — Position trace
  • GET /api/data-report — RPC stats and data quality
  • GET /api/health — Health check, snapshot age, memory usage
  • POST /api/refresh — Protected snapshot regeneration (requires Authorization: Bearer <API_SECRET>)

Production deployment guide: dashboard/DEPLOYMENT.md

JSON Snapshot

Export engine output as JSON:

# Default output to dashboard/data/snapshot.json
npm run snapshot

# Custom output path
npm start -- --json /path/to/output.json

Live Mode

Subscribes to WebSocket account changes for real-time monitoring:

npm start -- --subscribe

Type Check

npm run lint

Build

npm run build

Architecture

                    Solana Mainnet
                         |
                    [RPC / WSS]
                         |
              +----------+----------+
              |                     |
         FlashDecoder          FlashSubscriber
         (decoder.ts)          (subscriber.ts)
              |                     |
              |   Anchor IDL        |   WebSocket
              |   Deserialization   |   Account Changes
              |                     |
              +----------+----------+
                         |
                   Decoded Accounts
                   (Pool, Custody, Market,
                    Position, CustomOracle)
                         |
              +----------+----------+
              |          |          |
         risk-engine  oracle.ts  metrics.ts
              |          |          |
         Per-position  Price     Aggregation
         PnL, margin,  banding,  density maps,
         leverage,     exit      histograms,
         liquidation   pricing,  utilization,
                       lock fee  borrow rate
              |          |          |
              +----------+----------+
                         |
                    guards.ts
                    Runtime invariant
                    assertions
                         |
                      cli.ts
                      Output formatting,
                      audit mode,
                      sanity checks,
                      presentation

Layer Separation

I/O Layer (decoder.ts, subscriber.ts) -- Handles all network communication with Solana. Rate-limit-aware batched fetching. Anchor IDL-based deserialization.

Pure Computation Layer (risk-engine.ts, oracle.ts) -- Deterministic, side-effect-free functions. All math uses BN arbitrary-precision integers. Every function documents its Rust source, input units, and output units.

Aggregation Layer (metrics.ts) -- Computes protocol-wide metrics from individual position risk assessments. Bucketing, histograms, summary statistics.

Guard Layer (guards.ts) -- Runtime invariant assertions at critical computation points. Catches impossible states from corrupted data or engine bugs.

Presentation Layer (cli.ts) -- Argument parsing, output formatting, audit mode, sanity checks, and the final risk summary.

See docs/architecture.md for detailed data flow documentation.


Limitations

The Flash Trade core program (perpetuals-closed.git) is closed-source. All formulas in this engine are derived from the publicly available reference implementation and SDK. The following are known areas of potential divergence:

  1. Production core program -- Closed-source. May contain logic not present in the reference implementation.
  2. Degen mode -- degen_size_usd selects between maxLeverage and maxDegenLeverage. This engine uses maxLeverage exclusively.
  3. price_impact_usd -- Production may adjust entry prices based on market impact. This engine uses stored entryPrice.
  4. Unsettled values -- unsettled_value_usd and unsettled_fees_usd exist on position accounts but settlement logic is not in the reference implementation.
  5. No prediction -- This engine computes current state only. It does not forecast liquidation cascades or price movements.
  6. Non-atomic snapshot -- On-chain state (pools, custodies, markets, oracles, positions) is fetched sequentially via RPC. Between fetches, on-chain state can change. The engine mitigates this by skipping positions with unresolved references, but computed metrics reflect a best-effort snapshot, not an atomic point-in-time view.

See docs/known-limitations.md for complete documentation.


Data Integrity Policy

All computations in this engine are subject to the following constraints:

  1. Live mainnet RPC only. Every account value (pool, custody, market, position, oracle) is fetched from Solana mainnet via RPC at runtime. No values are cached between runs.

  2. No synthetic data. No hardcoded prices, no default oracle values, no placeholder accounts. If an oracle returns zero, the computation throws. If an account is missing, the position is excluded and logged.

  3. No default values assumed. Oracle exponents are read from each custody's oracle account dynamically. Token decimals come from the custody account. Leverage thresholds come from custody pricing parameters. Nothing is inferred or assumed.

  4. Invalid or stale data results in exclusion. Each position is classified with a DataIntegrity status:

    • valid — All inputs verified from live on-chain data.
    • stale_oracle — Oracle publishTime exceeds maxPriceAgeSec. Position is excluded from liquidation computation.
    • incomplete_data — Missing custody, market, or oracle account. Position is skipped entirely.
    • invalid_data — Corrupted or zero oracle price, zero entry price, or unresolvable market side. Position is skipped with error logged.
  5. Snapshot is non-atomic. On-chain state is fetched sequentially via RPC. Between fetches, state can change. Computed metrics reflect a best-effort snapshot, not an atomic point-in-time view. Positions with unresolvable cross-references are excluded rather than filled with synthetic values.

  6. Failure is explicit. Every skipped position is counted and categorized by reason (zero_size, missing_market, invalid_side, missing_custody, missing_oracle, computation_error). The --data-report flag outputs the complete failure breakdown. No failures are silently absorbed.


Project Structure

flash-risk-engine/
├── src/
│   ├── cli.ts              Entry point, output formatting, audit mode
│   ├── risk-engine.ts      Pure risk computation (PnL, margin, leverage, liquidation)
│   ├── oracle.ts           Oracle pricing, exit price, trade spread, lock fee
│   ├── metrics.ts          Aggregated metrics (density, histograms, utilization)
│   ├── guards.ts           Runtime invariant assertions
│   ├── snapshot-service.ts   JSON snapshot generator for dashboard
│   ├── audit.ts            Adversarial audit script (standalone deep trace)
│   ├── liquidation-proof.ts  Liquidation proof generator (standalone)
│   ├── types.ts            TypeScript interfaces matching IDL account layouts
│   ├── constants.ts        Verified precision constants with source references
│   ├── decoder.ts          Anchor IDL-based account deserialization
│   ├── subscriber.ts       WebSocket live update handler
│   ├── pool-config.ts      Mainnet pool addresses
│   └── idl/
│       └── perpetuals.json IDL (downloaded via setup script)
├── dashboard/                         Web visualization console (Next.js)
│   ├── src/
│   │   ├── app/
│   │   │   ├── page.tsx               Main dashboard page
│   │   │   └── api/                   REST API routes
│   │   │       ├── snapshot/route.ts  GET /api/snapshot
│   │   │       ├── trace/route.ts     GET /api/trace?position=
│   │   │       ├── data-report/route.ts GET /api/data-report
│   │   │       ├── health/route.ts    GET /api/health
│   │   │       └── refresh/route.ts   POST /api/refresh (protected)
│   │   ├── middleware.ts              Rate limiting, request logging
│   │   ├── components/                React components
│   │   └── lib/
│   │       ├── types.ts               Serializable API types
│   │       └── env.ts                 Server-side environment validation
│   ├── scripts/
│   │   ├── start-production.sh        Production startup script
│   │   ├── refresh-snapshot.sh        Cron-compatible snapshot refresh
│   │   ├── load-test.sh               Concurrent load simulation
│   │   ├── fre-dashboard.service      systemd unit (dashboard server)
│   │   ├── fre-refresh.service        systemd unit (snapshot refresh)
│   │   └── fre-refresh.timer          systemd timer (5-min interval)
│   ├── data/
│   │   └── snapshot.json              Generated snapshot (gitignored)
│   ├── DEPLOYMENT.md                  Production deployment guide
│   └── .env.production.example        Environment variable template
├── docs/
│   ├── architecture.md     Data flow and layer separation
│   ├── mathematical-model.md Full derivations and worked examples
│   ├── verification.md     Verification methodology and evidence
│   └── known-limitations.md Production divergence documentation
├── scripts/
│   └── setup.sh            IDL download and verification
├── package.json
├── tsconfig.json
├── LICENSE
└── CONTRIBUTING.md

Disclaimer

Flash Risk Engine is an independent analytical tool. It is not affiliated with, endorsed by, or maintained by Flash Trade or any related entity. The engine reads publicly available on-chain data and applies formulas derived from publicly available source code. It makes no guarantees about the accuracy of its outputs relative to the production Flash Trade program, which is closed-source.

Use at your own risk. This software is provided as-is, without warranty of any kind.


License

MIT. See LICENSE.

About

Read-only risk intelligence engine for Flash Trade perpetuals on Solana

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages