FlareWatch 为每个 P-Chain 验证人分配一个 0–100 的综合评分,跨越 9 个维度。数学计算是确定性的,输入来自公开链数据 [Flare Explorer] [FSE] [Flaremetrics],相同的算法适用于网络上的每个验证人——包括 FlareWatch 自己的验证人节点,它由这个确切函数评分,不获得特殊处理。本页面记录每个维度和阈值,以便运营者和质押者能够准确了解评分如何计算以及为什么选择每个数值。这里的每项声明都链接回其主要的链上或上游来源——请参阅底部的 来源与参考。
质押模式 中看到的内容(向 P-Chain 验证人委托 FLR 以获得 VRM + MIRROR 奖励)。委托模式 中显示的 FTSO 提供商评分(向 FTSO 数据提供商委托 WFLR)使用独立的 13 维算法,重点关注数据提供商性能——准确性、V2 协议参与等。这是两个不同的链上角色,具有不同的奖励,单独评分。请参阅 FTSO 提供商评分方法论 了解委托方面。质押表中的 APY 是委托人获得的全额收益率——单一数字,无需心算。它是通过 Flare 奖励脚本测量的(实际支付而非公式),扣除验证者费用后,每个纪元随实际奖励变动。在 FlareWatch 上,APY 表示费用后,APY 表示费用前。
与Flare Systems浏览器比较? FSE和其他浏览器显示仅委托费率 — 他们不添加MIRROR — 所以我们的总APY在任何MIRROR活跃验证人上读取更高(间隙正好是上面的MIRROR行)。两个数字都是来自相同奖励脚本数据的约8周期移动平均数,所以验证人的窗口中期费用变化在任一站点上的当前费用快照后滞后,直到它在窗口中老化。
APY 工具提示中出现的另外两个数字不是委托人的收益率:理论基线(网络总体 APY × (1 − 费用),仅质押——在积累足够的测量历史之前用作备选方案),以及运营者的自有资金收益(验证者自身的质押回报,由费用捕获放大——运营者指标,而非您的收益)。
对于评分:净收益维度仅评分VRM委托费率,MIRROR在其自己的维度中被评分 — 所以MIRROR从不被双倍计算,即使它包含在显示的总APY中。
| 90+ | 顶级 — 排名前约 10–20% 的运营者。典型特征:全栈验证人 + FTSO + FDC、低端费用、MIRROR 活跃、一致的 FIP-10 可靠性、健康的委托者基础、有意义的自我质押。不需要单一维度 — 运营者通过在大多数类别中堆积优势来达到顶级。 |
| 80–89 | 强劲 — 符合大多数关键基准;距离顶级还差一两个维度。 |
| 70–79 | 良好 — 符合所有基线标准;无重大缺陷。 |
| 60–69 | 可接受 — 可用但没有区分。 |
| <60 | 低于中位数 — 一个或多个维度存在显著缺陷。数学事实,不是质量判断。 |
if (uptime >= 99.5) raw = 17 + (uptime - 99.5) * 6
else if (uptime >= 99) raw = 13 + (uptime - 99) * 8
else if (uptime >= 95) raw = 4 + (uptime - 95) * 2.25 // v3.9: was (u - 95) * 3.25 starting at 0
else raw = max(0, uptime - 90) * 0.8 // 90 → 0, 95 → 4 (continuity)
raw = clamp(raw, 0, 20)
reliability = epochsIncluded / epochsObserved // last ~8 epochs · v4.2: the
// FULL FIP-10 minimums set
// (uptime + FSP + FTSO + FDC),
// not RPC-uptime alone
score = raw * clamp(reliability, 0, 1) // dimension max 20scoreAPR = delegationAPY > 0 ? delegationAPY : baseAPR // VRM, net of fee medianAPR = median(scoreAPR across all validators) cappedAPR = min(scoreAPR, 25) // APY_DISPLAY_CAP if (medianAPR > 0): ratio = cappedAPR / medianAPR score = clamp(((ratio - 0.6) / 0.6) * 18, 0, 18) else: score = min(18, (cappedAPR / 8) * 18) // fallback: BASE_APY = 8
// anchor = max(observed minimum active fee, 20% protocol floor) d = fee - anchor // distance above market best if (d <= 0) score = 7 // at/below best available else if (d <= 5) score = 7 - d * 0.2 // 0 → 5 over: 7 → 6 else if (d <= 10) score = 6 - (d - 5) * 0.3 // 5 → 10 over: 6 → 4.5 else if (d <= 15) score = 4.5 - (d - 10) * 0.4 // 10 → 15 over: 4.5 → 2.5 else if (d <= 20) score = 2.5 - (d - 15) * 0.5 // 15 → 20 over: 2.5 → 0 else score = 0 // fees > anchor+20 saturate at 0/7 — the v4.5 extreme-fee // penalty below takes over from there
// Verification baseline (v3.10 hybrid) isCurated = (in KNOWN_VALIDATORS) OR ( daysObserved >= 90 AND operatorDelegatorCount >= 25 AND retention30d >= -15% AND selfBondFLR >= 1_000_000 ) if (isCurated) verification = 7 else if (name auto-discovered) verification = 3 else verification = 0 // FTSO-derived (only when ftsoOperatorScore is a number) clamped = clamp(ftsoOperatorScore, 50, 100) ftsoDerived = 4 + (clamped - 50) * (8/50) // FTSO 50 → 4, FTSO 100 → 12 // Final score score = max(verification, ftsoDerived)
passthrough = max(0, 1 - fee / 100) if (mirrorStatus == "active") base = 10 * passthrough else if (mirrorStatus == "paused") base = 5 * passthrough else if (mirrorStatus == "inactive") base = 0 else base = 5 * passthrough // no data yet // v4.6 — capped, median-anchored bonus for delivered MIRROR yield. // Only mirror-active validators paying ABOVE 1.2x the network median // delivered rate (mirrorAPY, net of fee) earn it; size-neutral. ratio = mirrorAPY / networkMedianMirrorAPY bonus = clamp((ratio - 1.2) * 5, 0, 2) // active only, else 0 score = base + bonus // dimension max 12
utilization = clamp(1 - freeSpaceFLR / maxDelegationFLR, 0, 1) if (utilization <= 0.70): score = 1.75 + (utilization / 0.70) * 5.25 // 1.75 → 7 ramp else: score = 7 - ((utilization - 0.70) / 0.30) * 1.75 // 7 → 5.25 ramp
// Count signal (max 6)
if (delegatorCount <= 5) countScore = 0
else if (delegatorCount >= 500) countScore = 6
else countScore = clamp(log(delegatorCount / 5) / log(100) * 6, 0, 6)
// Concentration adjustment (-1 to +1) — skip if <3 delegators
avgFLR = delegatedFLR / delegatorCount
if (avgFLR < 500_000) concentrationAdj = +1
else if (avgFLR < 5_000_000) concentrationAdj = +0.5
else if (avgFLR > 50_000_000) concentrationAdj = -1
else if (avgFLR > 20_000_000) concentrationAdj = -0.5
else concentrationAdj = 0
// Longevity bonus (0 to +1)
daysObserved = (now - firstObservedAtMs) / 86400000
if (daysObserved >= 90) longevityBonus = +1
else if (daysObserved >= 30) longevityBonus = +0.5
else longevityBonus = 0
// Self-bond alignment (-1 to +2, two-axis since v4.7)
if (selfBondFLR < 1_000_000) selfBondAdj = -1 // hollow-operator floor
else:
ratio = selfBondFLR / totalStake // totalStake = selfBond + delegated
proportional = ratio >= 0.10 ? +2 : ratio >= 0.05 ? +1 : 0
absolute = min(1, selfBondFLR / 20_000_000) * 2 // saturates at top-decile bond
selfBondAdj = max(proportional, absolute) // the better of the two axes
// Retention (-0.5 to +0.5, v3.7) — skip if no 30-day baseline yet
delta = (delegatedFLR - delegatedFLR30dAgo) / delegatedFLR30dAgo
if (delta >= +0.10) retentionAdj = +0.5
else if (delta <= -0.15) retentionAdj = -0.5
else retentionAdj = 0
// Self-bond trajectory (-0.5 to +0.5, v3.7) — skip if no baseline
sbDelta = (selfBondFLR - selfBondFLR30dAgo) / selfBondFLR30dAgo
if (sbDelta >= +0.20) selfBondTrajectoryAdj = +0.5
else if (sbDelta <= -0.10) selfBondTrajectoryAdj = -0.5
else selfBondTrajectoryAdj = 0
score = clamp(countScore + concentrationAdj + longevityBonus + selfBondAdj
+ retentionAdj + selfBondTrajectoryAdj, 0, 11)// Time-weighted rate is computed upstream from per-epoch // reward-scripts data with decay = 0.85 per epoch back. r = deliveryRatio // capped at 1.0 if (r >= 1.00) base = 10 else if (r >= 0.97) base = 9 + (r - 0.97) * (1 / 0.03) // 0.97 → 9, 1.00 → 10 else if (r >= 0.95) base = 8 + (r - 0.95) * (1 / 0.02) // 0.95 → 8, 0.97 → 9 else if (r >= 0.90) base = 6 + (r - 0.90) * (2 / 0.05) // 0.90 → 6, 0.95 → 8 else if (r >= 0.85) base = 4 + (r - 0.85) * (2 / 0.05) // 0.85 → 4, 0.90 → 6 else if (r >= 0.80) base = 2 + (r - 0.80) * (2 / 0.05) // 0.80 → 2, 0.85 → 4 else base = max(0, r * 2.5) // 0 → 0, 0.80 → 2 // Variance penalty (CoV = std-dev / mean across per-epoch rates) variancePenalty = min(0.30, coefficientOfVariation * 0.5) score = base * (1 - variancePenalty) // Sample-size confidence dampener for < 3 epochs if (totalStakesCompleted < 3): confidence = totalStakesCompleted / 3 score = 5 + (score - 5) * confidence
daysLeft = (endTimeMs - now) / 86_400_000 if (daysLeft < 14) score = 0 else if (daysLeft < 30) score = 0 + (daysLeft - 14) * (2 / 16) // 14 → 0, 30 → 2 else if (daysLeft < 60) score = 2 + (daysLeft - 30) * (1 / 30) // 30 → 2, 60 → 3 else if (daysLeft < 120) score = 3 + (daysLeft - 60) * (2 / 60) // 60 → 3, 120 → 5 else score = 5
consecutiveMisses = 0 for entry in participation.recent (newest-first): if entry.eligible: break consecutiveMisses++ if consecutiveMisses < 2: penalty = 0 elif consecutiveMisses == 2: penalty = 3 elif consecutiveMisses == 3: penalty = 6 else: penalty = 10 // 4+ score = max(0, positiveDimensionsSum - penalty)
// v4.5 — fees beyond the Fee dimension's range (anchor+20)
if fee <= 50: penalty = 0
else: fraction = min(1, (fee - 50) / 50) * 0.75
penalty = positiveDimensionsSum * fraction
// fee 50% → no change · 75% → −37.5% of score · 100% → −75%
score = max(0, positiveDimensionsSum - outagePenalty - penalty)delegationFee、selfBond、delegatedStake和FTSO分数(如果您也是数据提供者)。generated-files/reward-epoch-N/nodes-data.json下。计算您的nodeID有多少个轮次具有uptimeEligible: true。该比率驱动您的运行时间维度乘数。RewardClaimed事件,其中claimType=3引用您的nodeID。如果最近没有,您将显示为MIRROR非活跃。GET /api/validators/{nodeID}/score-breakdown检索持久细分 — 每个维度的值、生成它的算法版本和最近的分数历史 — 作为JSON。UI中的分数细分面板从相同来源读取。// Step 1 — Raw composite (sum of dimensions, minus the two penalties)
positive = uptime + netYield + fee + operatorQuality
+ mirror + capacity + trust + delivery + timeRemaining
// Penalties (see the two penalty cards above):
// v4.3 active-outage streak — 0-1 missed epochs → 0, 2 → 3, 3 → 6, 4+ → 10
// v4.5 extreme fee (>50%) — positive × min(1, (fee - 50) / 50) × 0.75
raw = clamp(positive - streakPenalty - extremeFeePenalty, 0, 100)
// Step 2 — Smooth across recent cron snapshots (v3.5)
// Weights: current 0.5, prev1 0.3, prev2 0.15, prev3 0.05
smoothed = 0.5*raw + 0.3*prev1 + 0.15*prev2 + 0.05*prev3
// Step 3 — Apply sudden-change penalty if flagged this run
// Triggers: fee +50% or +5pt jump, self-bond -20% drop, uptime -5% crash
// Magnitude: -10 pts on detection, decays -7, -4, -1 over 3 runs
suddenPenalty = -10 if any flag triggered else (decaying remainder)
// Step 4 — Final score
score = clamp(smoothed + suddenPenalty, 0, 100)services/validators/scoring.ts 中实现。想要直接检查实现(而不是阅读上面的说明和公式)的运营者或研究人员,或想要为自己的使用而分叉它的人员,可以发送电子邮件至 [email protected] 请求访问权限。如果真有需求,我们将发布该文件作为独立开源包。/api/cron/refresh-validators 处的 cron 每 5 分钟重新计算每个活跃验证者的分数。输入(P-Chain RPC 读取、Flaremetrics、FSE、reward-scripts)在每次运行时都会重新获取。Flare 不会削减验证者质押。验证者不当行为的整个惩罚机制是奖励没收加上 FIP-10 passes 系统。没有双签削减、没有歧义削减、没有需要我们追踪的质押破坏事件。未能满足 FIP-10 最低条件的验证者会失去该 epoch 的奖励(如果通过数为零则全部没收,否则每个失败的协议失去一个通过);他们的质押本金保持不变。
这意味着该分数没有"罚没历史"维度——没有此类历史记录可追踪。我们追踪的是每次最小故障的后果:验证者在该轮次中获得零收益,这反映在epochsIncluded / epochsObserved中。2026-05-14的v4.2更新扩展了分数的可靠性乘数,使其在整个FIP-10最小值集合(正常运行时间、FSP签名、FTSO提交率、FDC参与)中使用该比率,因此验证者未能满足任何最小值要求都会在
FIP-10 最低阈值,来源于 dev.flare.network/network/fsp/rewarding:质押需要 80% 的正常运行时间 + 100 万 FLR 活跃自身质押;FTSO 锚定价格源需要估计值在共识中位数的 0.5% 以内,且达到 80% 的轮次;FTSO 块延迟价格源需要提交 80% 的预期更新;FDC 需要参与 60% 的投票轮次。达到 80% 正常运行时间 + 100 万自身质押底线但低于 300 万 / 1500 万收益阈值的验证者仍然会获得奖励,但不能积累通过——这个灰色地带通过此卡片的 passEligibility: "at-risk" 分类来体现。