FlareWatch memberikan setiap penyedia data FTSO skor komposit 0–100 di seluruh 12 dimensi terukur. Matematikanya deterministik, input adalah data on-chain publik dan ekosistem Flare[Flaremetrics] [FSE] [Flare Explorer], dan algoritma yang sama berlaku untuk setiap penyedia — termasuk penyedia FTSO FlareWatch sendiri, yang dicore oleh fungsi eksak ini tanpa perlakuan khusus. Halaman ini mendokumentasikan setiap dimensi dan threshold sehingga delegator dan operator dapat melihat dengan tepat bagaimana skor dihitung dan mengapa setiap nilai dipilih. Setiap klaim di sini menghubungkan kembali ke sumber upstream utamanya — lihat Sumber & referensi di bagian bawah.
delegation mode di halaman validator (mendelegasikan WFLR ke penyedia data FTSO untuk bagian inflasi FTSO). Skor validator yang ditampilkan dalam staking mode (mendelegasikan FLR ke validator P-Chain untuk reward VRM + MIRROR) menggunakan algoritma 9-dimensi terpisah yang berfokus pada operasi validator — uptime, fee, keandalan, dll. Ini adalah peran on-chain yang berbeda dengan reward yang berbeda, dinilai secara terpisah. Lihat Validator Score Methodology untuk sisi staking.| 90+ | Tingkat teratas — teratas ~10–20% penyedia FTSO. Profil tipikal: tingkat reward di atas median, akurasi tinggi, partisipasi protokol V2 penuh (FTSO Scaling + Fast Updates + FDC), fee rendah/nol, basis delegator besar, node validator yang membayar MIRROR, merek bernama. Tidak ada dimensi tunggal yang diperlukan — penyedia mencapai tingkat teratas dengan mengumpulkan kekuatan di sebagian besar kategori. |
| 80–89 | Kuat — memenuhi sebagian besar benchmark utama; satu atau dua dimensi kurang dari tingkat teratas. |
| 70–79 | Baik — memenuhi semua kriteria baseline; tidak ada celah besar. |
| 60–69 | Dapat diterima — dapat digunakan tetapi tidak terdiferensiasi. |
| <60 | Di bawah median — celah signifikan di satu atau lebih dimensi. Fakta matematis, bukan penilaian kualitas. |
if (rate <= 0 || medianRate <= 0): score = 0 else: ratio = rate / medianRate score = min(25, round(ratio * 12.5 * 10) / 10) // 1 decimal place // Anomaly detection: providers > 3× the median are capped at // median × 3 for scoring purposes (prevents data outliers from // distorting the linear curve).
if (fseAccuracySecondary > 0): pct = fseAccuracySecondary / 100 if (pct >= 97) score = 25 elif (pct >= 95) score = 18 + (pct - 95) * 3.5 // 95 → 18, 97 → 25 elif (pct >= 93) score = 16 + (pct - 93) * 1 // 93 → 16, 95 → 18 elif (pct >= 90) score = 13 + (pct - 90) * 1 // 90 → 13, 93 → 16 elif (pct >= 85) score = 10 + (pct - 85) * 0.6 // 85 → 10, 90 → 13 elif (pct >= 80) score = 6 + (pct - 80) * 0.8 // 80 → 6, 85 → 10 else score = max(0, 2 + (pct - 70) * 0.4) elif (fseAccuracyPrimary > 0): // Primary fallback: similar piecewise linear curve. else: score = WEIGHT_ACCURACY / 2 // Neutral — no FSE data
earned = last 12 epochs where rewardRate > 0, keyed by epochId pairs = [earned[i-1], earned[i]] where epochId[i] - epochId[i-1] == 1 if (pairs.length < 3) return 10 // Neutral — too few consecutive pairs // DOWNSIDE volatility only: rises never hurt, so a stable or RISING // rate scores near full and a recovery lifts the score. Only drops // between CONSECUTIVE epochs count, in proportion to depth (RMS), // weighted toward recent pairs so an active recovery pulls the score up. drops[i] = max(0, (prev - cur) / prev) // rise -> 0 w[i] = 0.8 ^ (age of pair) // newest pair = highest cv = sqrt( sum(w[i] * drops[i]^2) / sum(w[i]) ) score = max(0, round((1 - min(1, cv * 6)) * 20 * 10) / 10)
if (!isActive) score = 0 // see "Active" below
else:
score = 3 // active baseline
if (hasSubmitAddress AND hasSigningPolicyAddress AND voterRegistered):
score += 4 // V1 registered
if (fseFtsoScaling) score += 8/3 // ~2.67 each
if (fseFastUpdates) score += 8/3
if (fseFdc) score += 8/3
score = min(15, round(score * 10) / 10)anchor = max(lowest active fee observed, protocol fee floor) // FIP-16 sets a 20% minimum entity fee. All 98 providers charge // exactly 20%, so the anchor is 20% and nobody is docked for // charging the only fee the protocol permits. Same curve and // same anchor the validator page's Fee dimension uses. if (!isActive) score = 0 d = fee - anchor // distance ABOVE the best real offer if (d <= 0) score = 15 // at or below the anchor → full marks elif (d <= 5) score = 15 - d * 0.43 elif (d <= 10) score = 12.9 - (d - 5) * 0.64 elif (d <= 15) score = 9.6 - (d - 10) * 0.86 elif (d <= 20) score = 5.4 - (d - 15) * 1.07 else: score = 0 // extractive
if (no fseNodeIDs) score = 0 if (mirrorStatsMap empty) score = 12 / 2 = 6 // Neutral seed before data lands activeCount = nodes_with_status_active fraction = activeCount / fseNodeIDs.length base = 12 * fraction // Overperformance bonus per node, applied only when ALL gates pass: // - >= 30 days observed since first reading (firstObservedAtMs) // - >= 3 paid stake observations // - finite medianOverpaymentRatio // Bayesian shrinkage with prior k=5 toward 1.0 (neutral): // shrunken = (observedRatio * N + 1.0 * 5) / (N + 5) // Bucketed bonus from shrunken: // < 1.05 → 0 < 1.15 → 1 < 1.30 → 2 >= 1.30 → 3 avgBonus = sum_per_node(bonus) / fseNodeIDs.length score = round((base + avgBonus) * 10) / 10
if (count <= 5) score = 0 elif (count >= 500) score = 12 else: ratio = log(count / 5) / log(100) // maps [5, 500] → [0, 1] score = round(min(12, max(0, ratio * 12)) * 10) / 10
if (fseActive) score = 10 elif (rewardRate > 0) score = 7 else score = 0
change = abs(votePowerDailyChangePct * 100) if (change < 1) score = 10 elif (change < 3) score = 10 - (change - 1) * 1.5 // 1 → 10, 3 → 7 elif (change < 5) score = 7 - (change - 3) * 1.5 // 3 → 7, 5 → 4 elif (change < 10) score = 4 - (change - 5) * 0.4 // 5 → 4, 10 → 2 else score = max(0, 2 - (change - 10) * 0.1)
if (no fspData OR totalEpochs <= 0) score = 5 // active window = epochs from the provider's first paid epoch to now missed = (active-window epochs) - (epochs the provider was paid) score = max(0, 10 - missed * 3)
if (no name) score = 0 elif (name starts with 0x or matches hex regex) score = 0 elif (name.length < 4) score = 4 else score = 8
ownBond = operator's own P-Chain node bond (FLR) total = ownBond + delegated WFLR vote power alignment = piecewise-linear ratio curve (0% → 0 … ≥10% → 7) absolute = min(7, ownBond / 5,000,000 * 7) // saturates at 5M FLR score = max(alignment, absolute)
fspRewardRate, delegationFeePercentage, wNatWeight, dan votePowerDailyChangePct Anda.providersuccessrate.secondary untuk akurasi, ditambah flag entityminimalconditionslatest.ftso_scaling / ftso_fast_updates / fdc untuk status V2.RewardClaimed dengan claimType=3 mereferensikan nodeID Anda. Jika tidak ada yang terakhir untuk salah satu node Anda, Anda akan ditampilkan sebagai MIRROR-tidak aktif pada skor.// Step 1 — Raw composite (sum of 12 dimensions, max 172)
raw = rewardRate + accuracy + consistency + v2 + fee + mirror
+ delegators + participation + stability + compliance
+ identity + selfBond
// An UNMEASURED reward rate is not a FAILED one. When a provider is
// demonstrably distributing but we hold no rate figure for it, the
// Reward Rate weight leaves the DENOMINATOR rather than scoring 0
// against it — we score what we measured, over what we could measure.
maxRaw = (rewardRate figure missing AND isActive) ? 172 - 25 : 172
normalized = round((raw / maxRaw) * 100)
// (The separate vote-power dilution penalty was REMOVED in v4.9. It
// double-counted: an over-cap provider already earns a below-median
// realized reward rate, so the median-anchored Reward Rate dimension
// docks it for dilution once. Cap proximity is now a neutral DISPLAY
// signal on every provider, not a second deduction in the score.)
// Step 2 — Dynamic weight redistribution
// Across the active provider set, dimensions where everyone clusters
// (stddev < 1.0) become non-discriminating. Their weight gets
// redistributed evenly across dimensions where the spread is wider
// (stddev >= 1.0). The redistribution recomputes the score:
//
// for each dim d:
// multiplier[d] = 1.0 if stddev[d] < 1.0
// = (weight[d] + bonus) / weight[d] otherwise
// bonus = sum(weights of low-stddev dims) / count(high-stddev dims)
//
// adjusted_raw = sum(breakdown[d] * multiplier[d])
// adjusted_max = sum(weight[d] * multiplier[d])
// final = round((adjusted_raw / adjusted_max) * 100)
// Step 3 — Final score (capped at 100)
score = min(100, final)services/ftso/scoring.ts di basis kode FlareWatch. Operator atau peneliti yang ingin menginspeksi implementasi secara langsung (bukan hanya membaca prose + formula di atas) — atau yang ingin memforknya untuk penggunaan mereka sendiri — dapat mengirim email ke [email protected] untuk meminta akses. Kami akan menerbitkan file sebagai paket open-source standalone jika ada permintaan nyata./api/cron/refresh-validators, yang menghitung ulang skor setiap penyedia aktif setiap 5 menit (run yang sama yang melakukan rescoring validator P-Chain). Input (Flaremetrics, FSE, FSP rewards, V2 RewardManager events) diambil segar pada setiap run.