Methodology version 2

How we measure an anchor.

Every anchor we measure, on mainnet and on testnet, gets a score card built from 30 days of checks and of ledger readings, by the same engine and the same numbers. It says how well the anchor did on what we measured, and, as a separate number, how much we measured. Nothing is guessed, traffic never adds points, and every number can be recomputed by anyone from what we publish.

A check like a wallet’s, every 20 minutes.

We call each anchor’s public API the way a wallet would, from a fresh throwaway wallet each time. On mainnet we stop before any money moves. An anchor that has not answered for a week is checked every six hours instead, until it answers again. Which steps apply depends on what the anchor’s own stellar.toml advertises.

  1. Step 1

    stellar.toml

    Fetch the anchor’s SEP-1 file. It must still advertise a SEP-6 or SEP-24 transfer server.

  2. Step 2

    /info

    Ask the transfer server what it supports. It must answer with JSON.

  3. Step 3

    Sign-in challenge

    Request a SEP-10 challenge and check that the anchor’s published key signed it.

  4. Step 4

    Sign-in token

    Sign the challenge with a throwaway wallet and exchange it for a session token.

  5. Step 5

    Deposit start

    Start a SEP-24 interactive deposit, receive its URL, then abandon it. No money moves.

On testnet, further. On testnet the same five steps run first, and then money actually moves: a fresh wallet funded by Friendbot opens a trustline, deposits with the anchor and withdraws back, and every payment is checked on the ledger rather than taken from the anchor’s word.

Failure: no answer, a server error, or an endpoint the anchor advertises that does not exist.

Up, but declined: the anchor understood and said no (for example “client_domain is required”). It is up; we just could not test further.

Inconclusive: when our own network is down, the check is thrown away. Our outage is never recorded as theirs.

What the ledger says, not what the API says.

An API that answers is not an anchor that works. In the same round we read the public ledger for every asset an anchor issues, because the ledger cannot be styled, cached or claimed. All of it is read-only: no key of ours signs anything on mainnet.

Outstanding supply

The total amount of the asset in existence: every trustline, plus claimable balances, liquidity pools, and balances held by Soroban contracts. That last part is what makes the number whole, because an anchor can issue and redeem entirely through a contract without a single classic payment.

A mint or a redemption moves this total to the seventh decimal. Two readings twenty minutes apart are identical only when nothing settled at all, so a total that never changes is not a quiet week, it is no money moving.

Issuance and redemption

Payments into and out of the issuing account over 14 and 30 days, counted separately: how often the asset was created, and how often it was destroyed.

An asset issued again and again but never redeemed is one users can get into and not out of. When the history is longer than we can page through, the counts are lower bounds and the flags stand down.

What it traded at

Daily candles of the asset against USDC for the last 7 days: the price it last actually changed hands at, and the worst peak-to-trough fall inside the week.

Resting orders can be withdrawn; a settled trade cannot. This is also the only way to see a token that fell hard and recovered, which an average price hides completely.

The reference rate

What one unit of the currency the asset claims is worth in USD, from Reflector’s on-chain oracle where it carries that currency, and from a published daily rate table otherwise.

A peg is a claim about the outside world, so it cannot be checked against another number of ours. Every sample names the source it used, and the card carries it.

A reading is not a history. Supply cannot be fetched backwards, so it is only worth judging once we have watched it ourselves: an asset needs at least 200 readings spread over at least 7 days before the frozen-supply gate is even asked of it. Until then it is shown and not scored. On the first day of sampling every asset looks frozen, and that says something about us, not about the anchor.

Four pillars, weighted.

Each pillar is a number from 0 to 100. The weights say how much each moves the score. Market applies only to an anchor that issues its own asset, declares what it is worth, and trades where we can see it; when it does not, its weight is spread over the other three.

When Market applies
  • Availability 45%
  • Speed 20%
  • Integrity 20%
  • Market 15%
When it does not
  • Availability 53%
  • Speed 24%
  • Integrity 24%
  • Market n/a

Availability

45% of the score, 53% without Market

Does it answer?

The share of checks that got an answer, over the last 7 days and the last 30, blended half and half so a fresh outage counts without forgetting the month. An anchor that answers but declines an anonymous wallet is up: that costs test depth (confidence), not availability.

Shaped by “nines”: 95% uptime means an hour and a quarter down every day, a poor anchor, not a 95.

Uptime to score
100%
100
99.5%
100
99%
90
97%
75
95%
60
90%
35
80%
10
0%
0

Speed

20% of the score, 24% without Market

How fast does each step answer?

For every successful check, the average time of the steps it completed, so an anchor we can test to step 2 compares fairly with one we test to step 5. Then the 95th percentile over 7 days: the slow tail a wallet actually waits through, not the average.

Straight line between the two points, flat outside. No successful check in 7 days scores 0.

p95 seconds per step to score
0.75s
100
5s
40

Integrity

20% of the score, 24% without Market

Does what it publishes hold up?

A weighted checklist, from the latest check (the signing key over 30 days). A check that could not run because an earlier step failed on the anchor’s side fails; one that does not apply, or that the anchor declined by policy, is left out of both sides of the ratio.

Checklist (weights out of 12)
stellar.tomlFetched, parsed, and advertises a SEP-6 or SEP-24 transfer server.1
CORS on the tomlThe toml is served with Access-Control-Allow-Origin, as SEP-1 requires, so browser wallets can read it.1
Signed sign-inThe SEP-10 challenge is signed by the SIGNING_KEY the toml publishes.3
Transfer server /infoReturns JSON listing at least one enabled asset.1
TLS certificateValid, and at least 14 days from expiry.1
Stable signing keyThe same SIGNING_KEY in every check of the last 30 days.2
Genuine assetsEvery listed asset is vouched for by its issuer: its home domain is this anchor, or lists the asset itself.3

Market

15% of the score

Does its own asset hold its peg?

Only for an asset the anchor issues itself and declares to be worth one unit of a currency. The price against USDC on the Stellar DEX, compared with a reference rate for that currency. Three price sources: the order book when it is tight and deep, the AMM pool when it holds enough, and the price the asset last actually traded at. The traded price has no depth threshold, because a settled trade cannot be withdrawn the way a resting order can.

Not applicable (n/a) for an anchor that only distributes someone else’s asset, whose asset claims no peg, whose currency we have no reference rate for, or that nothing traded; its weight then moves to the other three.

Median deviation to score
0 bps
100
25 bps
100
50 bps
90
100 bps
70
200 bps
45
300 bps
25
500 bps
0
  • Then subtract
  • −20More than a quarter of the samples sat over 0.5% off the reference rate.
  • −20A gap over 1% lasted more than 6 hours: arbitrage had time and did not close it.
  • −20More than a quarter of the samples sat over 0.5% below the peg.

Confidence: how much we measured.

A score from three days of checks and one from a month are not equally sure. Confidence is a second number, from 0 to 100, and it is what separates ‘we know little’ from ‘it is failing’.

What goes into it

  • Time watched: full after 14 days of monitoring.
  • Number of checks: full at 100 conclusive checks in 30 days.
  • Test depth: the share of its own steps we could complete. An anchor that declines anonymous wallets at sign-in can only be tested to step 2 of 5.
sufficiency = ½·min(1, days/14) + ½·min(1, checks/100)
depth       = ½ + ½·(steps tested / steps expected)
confidence  = 100 · sufficiency · depth

What it changes

0 to 39Not enough dataThe number is withheld; the flags are still shown.
40 to 69Low
70 to 89Medium
90 to 100High

Confidence also pulls the score toward the middle (see below), so a new anchor lands near 50, not at 100. Traffic enters here and nowhere else: more checks make us surer, never make the score higher.

From pillars to one score.

Three steps, in whole numbers, so anyone can check a published score from the card alone.

  1. 1. Weigh

    Weighted average of the pillars that apply, with the weights above.

  2. 2. Pull toward 50

    Mix with a neutral 50 by confidence: at confidence 100 the pillars decide alone, at 0 the score is 50. Unknown is not perfect.

  3. 3. Cap

    A hard failure caps the score whatever the average says. With several, the lowest cap wins.

Try it

Gates
Score
100
  1. 1. Weighted pillars: 100.00
  2. 2. Pulled toward 50 by confidence 100: 100% × 100.00 + 0% × 50 = 100
  3. 3. Gates: none active

Gates and flags.

A gate is a hard cap for a failure no average should hide: three of them come from the API, four from the ledger. Information flags cap nothing; they explain the card. Both are always shown, whatever the confidence.

GateWhenCap
OUTAGEThe last 3 checks all failed.50
SEP10_MISMATCHAny of the last 3 sign-in challenges was not signed by the published key.40
LOW_UPTIMEUnder 90% uptime this week, over at least 20 checks.60
DEPEGIts own fiat asset more than 3% off its peg for over 24 hours.50
ONE_WAY_FLOWIts own asset issued 5 or more times in 14 days and never redeemed.70
FROZEN_SUPPLYThe total amount of its own asset never changed over 30 days, across at least 200 readings: nothing was issued and nothing redeemed.70
VOLATILEIts own asset fell more than 15% peak to trough on the DEX in a week.60
InformationWhen
LOW_COVERAGEWe could test less than 60% of its steps, usually because it declines anonymous wallets.
SILENTNone of its own assets was issued or redeemed in 30 days.
NO_MARKETIt issues a fiat asset that claims a peg, but nothing traded and no market was liquid enough to measure it this week.

What never adds points.

Some signals are easy to show and easy to fake. They are context at most.

  • Transaction volume

    A payment on Stellar costs a fraction of a cent, so volume can be inflated by anyone, and a busy anchor is not a reliable one. Movement is read as a yes-or-no: money moved, or it did not.

  • Age

    An old, abandoned anchor is old. “On-chain since” is shown as context; our own monitoring time feeds confidence.

  • Stake

    Optional, and nobody is ever slashed. Shown only where an operator has staked.

  • Machine learning

    There is no labelled ground truth and the score must be explainable to the anchor it describes. Plain statistics: windows, percentiles, and a pull toward the middle.

When we are the ones who broke.

A check that failed because our own collector could not reach anything says nothing about the anchor. Counting it would be a lie about somebody else’s service, so we drop those rounds and publish what happened.

  • A round is only dropped for an anchor that answered us later. Each incident names a window and a later window in which the collector is known to have worked. An anchor that never answered us keeps its failures: we cannot tell an outage of ours from an anchor that is genuinely gone, so we do not pretend to.
  • A failure the anchor answered with always counts. A 404 or a 500 came from the anchor’s own server, so our side of the connection worked. Only failures where we never got an answer at all can be ours.
  • The list decides nothing by name. It names rounds, never anchors: which checks come out is worked out from the published probe log by a rule anyone can re-run. Dropping checks also costs the anchor a little confidence, because confidence counts how much we actually measured.
  • Every card says so. A card whose window lost checks lists the incident in its published inputs bundle, and its page says how many. The incidents themselves are at /incidents.json, beside the probe logs.

Don’t take our word for it.

Every score card goes on-chain with the SHA-256 of the inputs it was computed from, and the inputs are published. Nothing in the chain of evidence depends on trusting us.

  1. The headline can be checked from the on-chain card alone: its pillars, confidence and flags are stored with it.
  2. The pillars can be recomputed from the inputs bundle, whose hash is on-chain:cd services/aggregator && npm run verify-score -- <bundle hash> --anchor <anchor id>
  3. The bundle holds a digest of each day’s checks, which can be recomputed from the raw probe logs we publish, and each check has its own evidence document: the anchor’s own signed sign-in challenge.