docs / metrics & scoring

Metrics & scoring

A verifier returns two raw metrics plus one reference score. The metrics are the source of truth. The score is advisory.

Raw metrics (authoritative)

sats_bonded

Sum of confirmed, unspent UTXO values at the address — or, if the bond: extension is present, exactly that value (surplus balance is ignored). Integer, satoshis.

days_unspent

Floor of days since the earliest confirmation time among the bonded UTXOs. When bond: is present, computed via the oldest-first greedy selection rule — see below. Integer, days.

Both metrics are computed from live Bitcoin chain state at verify time. Verifiers MUST NOT cache them beyond a short UX window.

Reference score (advisory)

The protocol registers one scoring algorithm, v0, for UX comparability:

score_v0 = round( ln(1 + sats_bonded) × (1 + days_unspent / 30), 2 )

Typical range 10–250.

sats_bondeddays_unspentscore_v0 (approx)
10,0003018
100,0009046
1,000,000365182
100,000,000365243

Scores are not comparable across algorithms and SHOULD NOT be the basis of a gate decision. Gate on raw metrics (min_sats, min_days).

Tier helper (UX-only)

For badge rendering and tier labels, a thin helper is provided:

TierThreshold
bronze10k sats × 30 days
silver100k sats × 90 days
gold1M sats × 180 days
platinum10M sats × 365 days

These thresholds are suggestions, not protocol. A platform's "Gold" tier does not necessarily mean the same thing as another platform's.

The bond: extension — why it matters

Without bond:, sats_bonded is the sum of all confirmed UTXOs at the address. A wallet that holds 50M sats casually produces the same proof as one carefully bonding 1M — which is usually not what the signer meant.

With bond:, the signer declares their intended commitment. Verifiers then:

  1. Fail with bond_insufficient if confirmed balance < bond.
  2. Set sats_bonded := bond (surplus ignored).
  3. Compute days_unspent via oldest-first greedy selection:
    • Sort UTXOs by (block_height ASC, txid ASC, vout ASC).
    • Greedily take UTXOs until sum ≥ bond; call this set S_bond.
    • first_seen := max(confirmation_time(u)) for u ∈ S_bond (the youngest in S_bond).
    • days_unspent := floor((now - first_seen) / 86,400).

This rewards long-term holding while preventing rotating UTXOs from resetting age without changing bond.

Why not write your own scoring algorithm into the protocol

RPs have specialised needs:

  • Forums often want a simple binary threshold (sats >= 10k AND days >= 30).
  • Marketplaces weight time heavily — long commitment matters more than capital.
  • Airdrops weight capital — total stake determines allocation.
  • Lending may weight compound stake × time differently.

The protocol deliberately ships one reference algorithm and a tier helper. Everything else is RP policy. Don't publish app-specific scores as "registered" — they become walled gardens.

Edge cases

  • sats_bonded == 0 → status bond_zero. Attestation is valid; policy layer decides what 0 means.
  • Unconfirmed deposits → status bond_pending. Unconfirmed UTXOs never count.
  • Spent UTXOs → not counted. When the user spends, the proof's sats_bonded drops on the next verify.
  • Missing bond: on a large wallet → confirmed balance is used. Signer should use bond: to pin intent.

Further