WhirlScore
WhirlScore is a transparent 0–100 launch-quality score. It measures verifiable transparency, fairness, liquidity and reliability properties of a launch. It is never a prediction of price and never a recommendation. Every input is public chain or index data; anyone can recompute a score from the inputs published in the /api/v1/launches/:mint/whirlscore response.
Components and weights (graduated launches)
| Component | Weight | Inputs |
|---|---|---|
| Security & Graduation | 30 | mint authority revoked, no freeze authority, quote stock verified (Enabled + fingerprint unchanged), Orca pool reserved, pool matches config/mints/tick spacing, position permanently locked, current protocol version |
| Holder Distribution | 20 | Top-10 share, Top-20 share, HHI, creator share — all computed over holders excluding protocol vaults, Orca vaults, position accounts, treasury, buyback vaults and burn addresses |
| Liquidity Quality | 20 | locked liquidity (yes/no), liquidity / market-cap ratio (log-normalized), standardized 1% and 5% depth slippage from pool state |
| Trading Activity | 15 | 24h volume / liquidity (log-normalized), unique active wallets 24h, trade consistency (share of 1h buckets with ≥1 trade over 24h) |
| Fair Start Quality | 10 | finalized batch (yes/no), meaningful participants (deposits ≥ 0.1% of accepted total), largest allocation share, Fair Start concentration HHI |
| Reward Reliability | 5 | epochs funded / epochs expected, successful claims share, harvest cadence, missed/failed epochs |
Total = Σ component points, capped per component at its weight. Each sub-score is clamped to [0, weight] before summing so an invalid score is impossible.
Scoring rules
Security & Graduation (30)
| Check | Points |
|---|---|
| Mint authority revoked | 5 |
| No freeze authority | 4 |
Quote stock Enabled and fingerprint unchanged since launch | 5 |
| Orca pool reserved at committed price | 4 |
| Reserved pool verified (config, mints, tick spacing) | 4 |
| Position exists, liquidity > 0, permanent lock verified | 6 |
| Protocol version == current | 2 |
Holder Distribution (20)
top10 = share of eligible supply held by top 10 wallets (bps)
score_top10 = 8 * clamp((7000 - top10) / 5000, 0, 1) # 20% → 8 pts, 70%+ → 0
score_top20 = 4 * clamp((8500 - top20) / 5000, 0, 1)
score_hhi = 5 * clamp((2500 - hhi) / 2300, 0, 1) # HHI in bps² / 10_000 (0..10_000)
score_creator = 3 * clamp((2000 - creator_bps) / 1900, 0, 1)
Dust accounts (balance < 1e-6 of supply) are excluded before ranking.
Liquidity Quality (20)
locked = 8 if lock verified else 0
ratio = locked_liquidity_value / market_cap (both in quote units)
score_ratio = 6 * clamp(log10(ratio * 100 + 1) / log10(51), 0, 1) # 50% ratio saturates
score_depth = 6 * clamp((500 - slippage_bps_for_1pct_mcap_trade) / 450, 0, 1)
Trading Activity (15)
turnover = volume_24h / liquidity
score_turn = 7 * clamp(log10(turnover * 10 + 1) / log10(11), 0, 1)
score_wallets = 5 * clamp(log10(unique_wallets_24h + 1) / log10(1001), 0, 1)
score_consist = 3 * (active_hour_buckets / 24)
Unique wallets are counted from indexed trade signers; no Sybil detection is claimed.
Fair Start Quality (10)
finalized = 2 if batch finalized
participants = 3 * clamp(log10(meaningful_participants + 1) / log10(501), 0, 1)
largest = 3 * clamp((5000 - largest_allocation_bps) / 4500, 0, 1)
concentration = 2 * clamp((2500 - fair_hhi) / 2300, 0, 1)
Reward Reliability (5)
funded_ratio = epochs_funded / max(1, epochs_expected_since_graduation)
claims_ratio = successful_claims / max(1, claim_attempts)
score = 3 * funded_ratio + 1 * claims_ratio + 1 * (1 if last_harvest within 2 epochs else 0)
Provisional WhirlScore (before graduation)
Components that need post-graduation data (Liquidity Quality, Reward Reliability, and the lock checks of Security) are marked notApplicable. The provisional score is round(Σ applicable_points / Σ applicable_weights × 100) and is always labeled Provisional WhirlScore. The API returns applicableWeights so the re-weighting is auditable. Nothing unavailable is reported as zero.
Reproducibility
GET /api/v1/launches/:mint/whirlscore returns { total, provisional, components[], inputs, computedAt, version }. inputs contains every raw number used. The history table whirlscore_history stores each recomputation. The reference implementation lives in apps/keeper/src/whirlscore/compute.ts and is pure.