How it works
Health score & pipeline
The Ecosystem Health Score formulas and weights, how data flows from external APIs into the map, and how long each source is cached.
Raw API responses are normalised, weighted and combined into signals using openly documented formulas. Below are the scoring models as implemented in the platform.
Ecosystem Health Score
A composite score, updated daily, that measures the overall health of the Solana ecosystem across six independently sourced sub-scores. Each sub-score contributes a weighted share of the composite.
Composite = round( (OnChain × 0.28)
+ (Developer × 0.22)
+ (Social × 0.16)
+ (Projects × 0.14)
+ (Momentum × 0.12)
+ (News × 0.08) )
// Every sub-score is already bounded to [0, 100]
// Updated daily, stored in the ecosystem_scores table
On-chain score (28%)
// Solana historical TVL from DeFiLlama /v2/historicalChainTvl/Solana
// Log-scale base: $1B TVL → ~62, $4B → ~76, $8B → ~83, cap at 85
TVL_base = min(85, floor(40 + log10(TVL_USD / 1e8) × 22.5))
// 7-day change bonus
bonus = +10 if Δ7d > +10%
| +6 if Δ7d > +5%
| +3 if Δ7d > +2%
| 0 if Δ7d > -2%
| -4 if Δ7d > -5%
| -8 if Δ7d > -10%
| -15 otherwise
OnChainScore = clamp(TVL_base + bonus, 20, 95)
Developer score (22%)
Computed live from GitHub commit activity across a curated basket of active Solana repositories, sourced from Electric Capital's open-source ecosystem taxonomy and filtered for real recent activity (stars, push recency, not forked or archived).
Social score (16%)
// Per category, recent X posts are classified by an LLM
// into a 0–100 social sentiment score (category_momentum)
socialScores = [social score for each category on latest date]
SocialScore = round(avg(socialScores))
// Until the pipeline has data, the composite uses MomentumScore
// in its place, so the score doesn't jump when X data arrives
Project ecosystem score (14%)
// Derived from the number of active projects and the claim rate
claim_rate = claimed_projects / total_active_projects
ProjectScore = min(100, round(35 + total_active × 0.4 + claim_rate × 30))
Category momentum score (12%)
// Average of the latest momentum scores across categories
momentumScores = [score for each category on latest date]
MomentumScore = round(avg(momentumScores))
// DeFi / RWA / Infrastructure: DeFiLlama TVL 7d change
// NFTs: CoinGecko floor-price momentum, curated blue-chip collections
// Memecoins: CoinGecko meme-sector market cap change
// DePIN: CoinGecko market-cap-weighted basket (RENDER, GRASS, HNT, IO, more)
// Gaming: not scored; Solana gaming tokens are too small and illiquid
// for a meaningful signal, so it's left out instead of a placeholder
News score (8%)
// Average sentiment of the last 7 days of articles
// sentiment_score ∈ [-1.0, +1.0] (negative → positive)
raw_avg = avg(sentiment_score) for articles in last 7 days
normalised = (raw_avg + 1) × 50 // maps [-1,+1] → [0,100]
volume_bonus = min(20, article_count × 0.4)
NewsScore = clamp(normalised + volume_bonus, 0, 100)
Data pipeline
Data flows from external APIs through a normalisation layer into a managed Postgres database on DigitalOcean, and is served by Next.js Server Components through Drizzle ORM. Page-level data is never fetched in the browser.
- DeFiLlama
- CoinGecko
- Helius
- GetXAPI
- Exa
- GitHub
- Score computation
- Sentiment mapping
- Project linking
- Story dedupe
- ecosystem_scores
- signal_news
- projects
- news_project_mentions
- Server Components
- Suspense streaming
- Tagged caches
- Drizzle ORM
- Project pages
- NexusGraph
- Charts & gauges
- Research context
Caching
| Data | Refresh | How |
|---|---|---|
| DeFiLlama TVL and protocols | 2 hours | In-memory cache (responses exceed 2 MB) |
| CoinGecko SOL price | 5 minutes | In-memory cache |
| alternative.me Fear & Greed | 5 minutes | In-memory cache |
| Database reads | 5 minutes | Tagged cache, cleared as soon as the data changes |