[Product]
How It Works
A scan is a single synchronous request handled by POST /api/analyze. There is no queue, no background job, and no database. Every step below runs inline, and the response is the finished report.
The pipeline
Validate the address
The submitted address is checked against an EVM (0x + 40 hex chars) or Solana (base58, 32-44 chars) pattern before anything else runs. Anything else is rejected with a 400 response, no external calls made.
Locate the trading pair
Knovo calls Dexscreener for every pair matching the address, then falls back to a Dexscreener text search if no direct match exists. Matching pairs are sorted by liquidity (tiebreak: 24h volume), and the top pair becomes the source of truth for price, liquidity, volume, and transaction counts.
Read holder distribution
In parallel with scoring, Knovo asks Moralis for holder stats and the top 10 holders, using the Solana holder endpoints for Solana tokens, or the matching EVM endpoints (Ethereum, Base, BNB Chain, Polygon, Arbitrum, Avalanche) otherwise. If MORALIS_API_KEY is unset, the chain is unsupported, or the request fails, this step is skipped and the response simply omits holder data rather than failing the scan.
Calculate the risk score
Liquidity, market cap, holder concentration, trading activity, buy/sell pressure, volatility, and pool age are combined into an 0-100 score by a fixed, deterministic function. No model is involved. See Risk Score Methodology for the exact rules.
Generate the AI summary
The computed score and its breakdown are handed to OpenAI, which is instructed to explain the existing score in two short, trader-facing sentences. It is explicitly told not to invent or change the number. If OPENAI_API_KEY is missing or the call fails, Knovo falls back to a deterministic one-line summary built from the score and top risk reason.
Assemble the response
The score, breakdown, holder data, activity stats, and summary are packaged into one JSON response alongside a short analyst report (what happened, why it matters, what to verify next, final verdict).
Design principles
Deterministic score, generative explanation
The number that matters, the risk score, is produced by plain arithmetic over live data, not by a language model. The same inputs always produce the same score. OpenAI is only used to phrase an explanation of a score that has already been calculated, and it is explicitly instructed not to change it.
Degrade, don't fail
Optional data sources are wrapped so a missing API key or a failed third-party call never crashes the scan:
- No
MORALIS_API_KEY, unsupported chain, or a failed holder lookup →holderDistributionis returned asnulland a note is added todataAvailability. - No
OPENAI_API_KEYor a failed OpenAI call → the summary falls back to a deterministic sentence built from the score and the top risk reason.
A scan only fails outright if no address is provided, the address fails format validation, or no trading pair can be found on Dexscreener at all.
Nothing is persisted
Knovo has no database. Every scan reads live from third-party APIs, computes a result, and returns it. Nothing about the request is stored server-side.
Data flow at a glance
| Input | Source | Feeds into |
|---|---|---|
| Contract address | User input | Address validation, Dexscreener lookup, Moralis lookup |
| Price, liquidity, volume, txns | Dexscreener | Risk score breakdown, liquidity/activity sections, AI summary |
| Top holder %, top-10 %, holder count | Moralis | Risk score breakdown, holder distribution section, AI summary |
| Score + breakdown | lib/riskScore.ts | AI summary prompt, analyst report, response payload |
[ Reserved fields ]
launchIntelligence and riskVerdict fields for forward compatibility with upcoming launch-forensics features. They are currently always null and an empty string respectively. See Data Sources.