FlareWatch 为每个 FTSO 数据提供商在 12 个计分维度上分配 0-100 的综合评分。计算方法是确定性的,输入数据来自链上和 Flare 生态系统的公开数据。[Flaremetrics] [FSE] [Flare Explorer],相同的算法适用于每个提供者——包括 FlareWatch 自己的 FTSO 提供者,它由此精确函数评分且无特殊对待。此页面记录了每个维度和阈值,以便委托者和运营者可以准确看到分数如何计算以及为什么选择每个值。此处的每个声明都链接回其主要上游来源——见 来源与参考 在底部。
委托模式中看到的内容(将 WFLR 委托给 FTSO 数据提供商以获得 FTSO 通胀份额)。在质押模式中显示的验证器评分(将 FLR 委托给 P-Chain 验证器以获得 VRM + MIRROR 奖励)使用单独的 9 维算法,侧重于验证器运营 — 正常运行时间、费用、可靠性等。这些是不同的链上角色,拥有不同的奖励,评分也不同。有关质押方面的详情,请参阅验证器评分方法论。| 90+ | 顶级 — 排名前约 10–20% 的 FTSO 提供商。典型特征:高于中位数的奖励率、高准确度、完整的 V2 协议参与(FTSO Scaling + Fast Updates + FDC)、低/零费用、大型委托人基数、支付 MIRROR 的验证器节点、知名品牌。不需要单一维度 — 提供商通过在大多数类别中堆积优势来达到顶级。 |
| 80–89 | 强劲 — 符合大多数关键基准;一或两个维度不足以达到顶级。 |
| 70–79 | 良好 — 符合所有基线标准;没有重大差距。 |
| 60–69 | 可接受 — 可用但无差异化。 |
| <60 | 低于中位数 — 一个或多个维度存在重大差距。数学事实,而非质量判断。 |
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 和 votePowerDailyChangePct。providersuccessrate.secondary 获取精准度,加上 entityminimalconditionslatest.ftso_scaling / ftso_fast_updates / fdc 标志以确定 V2 状态。RewardClaimed 事件,claimType=3 引用你的 nodeID。如果你的任何节点最近都没有相关事件,你在分数中将显示为 MIRROR-不活跃。// 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 中实现。运营者或研究人员如果想直接检查实现(而不是阅读上面的说明和公式),或想为自己的使用而分叉 — 可以发送电子邮件至 [email protected] 请求访问。如果有真正的需求,我们将把该文件发布为独立的开源包。/api/cron/refresh-validators 内 cron 的一个阶段运行,每 5 分钟重新计算每个活跃提供商的分数(与重新评分 P-Chain 验证器的运行相同)。输入(Flaremetrics、FSE、FSP 奖励、V2 RewardManager 事件)在每次运行时新获取。