The method, the taxonomy, and its limits
Teaching document · Version 2.0 · universe: CoinGecko Top-1000
Price and market cap say nothing about the question that decides an asset's long-run fate:
Does the money a protocol earns reach the token holder — and by what route.
This is called value accrual. Consider an example.
Picture an exchange that charges a fee on every trade and earns $100M a year. It has a token. Does holding that token make you richer because the exchange earned $100M? The answer varies:
All three exchanges look identical in a report: "revenue $100M". To a holder they are three entirely different assets. The method exists to tell them apart.
The question splits into three independent parts that must not be mixed:
One protocol earns a lot and returns nothing. Another returns everything it earns, and earns almost nothing. A third "pays yield" by printing new coins — paying the holder with his own dilution. A single "yield" number hides all three cases. Hence three separate axes.
Five concepts, in plain terms.
Where the rest goes: on an exchange like Uniswap the user pays a fee, but most of it goes to liquidity providers — people who supplied capital so the trade could happen at all. That is their earning for the capital. It has nothing to do with the protocol's token. Same in lending: the borrower's interest goes mostly to lenders, and the protocol keeps a slice.
Practical consequence. Counting by fees inflates the picture: it credits the token with someone else's income. The whole method counts protocol revenue. The gap is often several-fold.
FDV ÷ market cap shows how many times supply can still grow:
This is called the overhang.
Team, investor and foundation coins are usually locked and released on a schedule. Each release is an unlock. A cliff is a single large release on one day — more dangerous than a smooth schedule, since the market takes the hit at once.
They run simultaneously, against each other:
Staking means locking a token for a payout. The question almost always skipped: where does the money for that payout come from. Two sources, opposite in meaning:
In the second case "12% APR" is an illusion. Stakers were rewarded at the expense of non-stakers, whose share was diluted. The pie did not grow — it was re-cut. Yield dashboards such as StakingRewards show the percentage without separating the source, and that is their central weakness.
Each axis scores 2 (good) / 1 (middling) / 0 (bad). Absent data yields no score at all (see §8).
Counted on protocol revenue, not fees (§2).
| score | in plain terms |
|---|---|
| 2 | Earns a lot (from $25M/year), the flow is steady, and revenue does not rest on speculation around the token itself |
| 1 | Earns moderately ($2–25M/year). Or earns a lot, but the flow is spiky or speculative |
| 0 | Earns almost nothing (under $2M), or the volumes are untrustworthy |
What "steady flow" means. Revenue can be a one-off spike: hype arrives, everyone trades for a week, then silence. We catch this by comparing paces:
Take the last 7 days of revenue and annualise it.
Take the last 30 days and annualise it.
Divide the first by the second.
Up to 2.5 counts as fluctuation. Above that, the top score is withheld.
The absolute level of inflation carries no meaning. Printing 9% a year is neither good nor bad by itself — what matters is whether that printing is offset. An asset inflating 9% that returns more to the holder than it prints creates no dilution. An asset inflating 3% that returns nothing dilutes by the full 3%.
So the axis measures a ratio, not a level:
issuance ($/day) = daily growth in circulating supply × price
offset ($/day) = payouts to the holder per day
coverage = offset ÷ issuance
| score | in plain terms |
|---|---|
| 2 | Circulating supply is shrinking (more is retired than printed) or payouts fully cover issuance (coverage ≥ 1) |
| 1 | Issuance is partially offset (coverage 0.3–1) |
| 0 | Issuance is barely offset (coverage < 0.3) — direct dilution |
If issuance cannot be measured, the score is 1. Ignorance is not punished.
What the issuance figure already accounts for. It is taken as the change in circulating supply, which makes it net. Everything that reduces circulation is already subtracted:
Counted separately is only the second kind of offset — compensation in money: payouts that do not shrink supply but return value to the holder.
What does not count as an offset:
FDV overhang and pending unlocks are excluded from axis B. They are potential, not actual: until coins reach the market, they have diluted no one. Penalising them means scoring something that has not happened.
They appear as a separate threat indicator: FDV overhang, supply outside circulation, absence of a supply cap. And when an unlock does occur, it shows up in the issuance measurement by itself — circulation jumps. No schedule is needed: a realised unlock is indistinguishable from any other issuance, which is correct in substance.
Why we do not use unlock schedules. DefiLlama's schedule moved behind a paid plan
(its endpoints return 402), and no free source exists. The loss is small: a schedule
describes what was promised, the supply measurement describes what happened.
| score | in plain terms |
|---|---|
| 2 | A real flow reaches holders: at least $1M/year and at least 0.5% of market cap |
| 1 | A mechanism exists but does not work: nothing to burn (no activity), or the payout comes from printing, or the launch is still ahead |
| 0 | No mechanism, or the money structurally bypasses the token |
Why two conditions. An absolute figure untethered from size deceives. A $1M flow against a $10B cap is 0.01% — a million on paper, nothing in practice.
CoinGecko publishes historical price and market cap series for free. Its market cap is price × circulating supply, so circulation follows by division:
circulating(t) = market_cap(t) ÷ price(t)
A 90-day series gives the actual growth in circulation — net issuance after everything that reduces it.
Verifying the source is mandatory. CoinPaprika computes market cap from **total
supply**, not circulating, so the same division returns a false zero there: for Canton
CoinGecko shows +8.3%/year and CoinPaprika 0.0%. The trick works only where market cap
is built on circulating supply.
The example this exists for. Canton:
| quantity | value |
|---|---|
| supply cap | ∞ — none |
| actual inflation | +9.0% per year (measured over 90 days) |
| issuance | $1.22M per day |
| payouts to holders | $1.39M per day |
| coverage | ×1.14 — issuance is offset |
Unlimited issuance looks like a death sentence for any token. Yet Canton prints 9% a year and returns more to the holder than it prints. Under "inflation above 8% scores zero" it would take the worst mark; under the ratio it takes the best — which matches substance.
The opposite case — MORPHO: inflation +103%/year, issuance $2.48M/day, payouts $0, coverage ×0.00. Zero, and deserved.
A third — HYPE: circulation shrinks 25% a year, because buybacks into the reserve withdraw coins from the market. The measurement catches this automatically; no special logic for treasury parking is required.
The axes say how well. The model says by what mechanism value reaches the holder.
| key | who receives | in what form | note |
|---|---|---|---|
direct_yield | stakers | money or another asset | closest to a dividend; independent of the token's price |
buyback_to_holders | stakers or treasury | the token | your gain depends on the token price, and the buyback supports that price |
buyback_burn | all holders | a growing share | passive; no staking required |
Examples: Pendle and Maple (direct_yield); Aave, Hyperliquid, Chainlink (buyback_to_holders); PancakeSwap, PUMP (buyback_burn).
Why these three are separated. In a "does it pay" table they look identical. To a holder
they differ: in the first you receive money, in the second a token (your gain tied to its
price), in the third nothing lands in your hands but your share grows. The popular
"real yield" label collapses all three.
Here what burns is not discretionary revenue but the transaction fee inside the network. The difference matters: this is a rule baked into the protocol, not a management decision that can be cancelled next quarter.
| key | mechanism | condition |
|---|---|---|
base_fee_burn | the base transaction fee is destroyed (EIP-1559) | needs real activity |
burn_and_mint | fees burn the token while new coins are minted to operators | deflation only if burning exceeds minting |
Examples: Ethereum, TRON, Monad, Plasma (base_fee_burn); Canton, Zama (burn_and_mint).
The fuel check. A burn mechanism can be perfectly written, but an empty network has
nothing to burn. If annual revenue is under 0.1% of market cap, the mechanism is idling:
the burn-no-fuel flag is set and axis C drops to zero.
| key | meaning |
|---|---|
monetary | The protocol does not capture revenue by design. Value rests on scarcity. Judging it by accrual is meaningless — its thesis is scarcity. Examples: Bitcoin-like assets, TAO |
governance_shell | The protocol earns, reports show revenue, but the token only grants a vote |
governance_shell is the market's most common trap. Ondo is the reference case: its methodology literally says "yield goes to token holders" — but those are holders of USDY and OUSG, Ondo's stablecoin products. The ONDO token receives nothing. The statement is true; the inference from it is false.
| key | meaning |
|---|---|
pre_revenue_infra | Tokenomics written, mechanism specified, protocol earns nothing yet |
inflation_staking | Staking pays, but pays in newly printed coins (§2) |
The distinction is critical: inflation_staking looks like a yielding asset at 8–15% APR, yet no outside money enters. Non-stakers fund stakers through dilution. Examples: Polymesh, Octra, most young PoS networks.
wrapper — the token tracks another asset one-to-one: stablecoins, liquid staking (stETH), wrapped coins (WBTC), tokenised stocks and funds. It has no accrual of its own; assigning it a tier would be a category error.
reflexive modifierNot an eleventh model — a flag on top of any of them.
Revenue is real, money genuinely arrives, but its volume is created by speculation around the asset itself: memecoin trading, agent tokens, perp venues paying incentives for volume.
An ordinary business earns on outside demand. Reflexive revenue arises from interest in the token itself: people speculate → fees rise → the protocol buys the token → the price rises → more speculators arrive. The loop feeds itself and collapses with attention, because no external demand sits under it.
Effect: caps axis A at 1 and blocks the top tier.
Why a flag. Reflexivity is perpendicular to mechanism. PUMP buys and burns (buyback_burn) and is reflexive. Making reflexive a type would force a choice between two true facts, or double the taxonomy into reflexive_buyback_burn, reflexive_buyback_to_holders, and so on.
Numbers alone cannot separate close models: buyback_burn, buyback_to_holders and direct_yield all show "holders revenue above zero". They are separated by the mechanism description — DefiLlama's methodology string, and where that falls short, the project's own tokenomics documentation.
Strict order:
wrapper. Stop.burn_and_mint · "base fee"/EIP-1559 → base_fee_burn · "gas fee" + burn → base_fee_burn · explicit "buy back and burn" → buyback_burn · buyback + stakers/reserve → buyback_to_holders · fees to holders without buyback → direct_yield.base_fee_burn; burn with minting → burn_and_mint; hard cap without revenue capture → monetary.governance_shell.inflation_staking; otherwise → pre_revenue_infra.reflexive flag is set independently of steps 1–5.Order inside step 2 carries meaning. An explicit "buy back and burn" is checked before "buyback + treasury". PancakeSwap's description mentions both; the treasury rule firing first would yield buyback_to_holders, which is wrong. Burning is the more specific signal.
The tier is computed by code, from a formula, not by judgment. The model owns the mechanism; the tier owns quality.
With a, b, c as the axis scores and total = a+b+c:
| tier | condition | meaning |
|---|---|---|
| T1 | all three axes = 2 | Earns, does not dilute, shares |
| T2 | c=2, a≥1, b≤1 | Shares materially, with a flaw in issuance or scale |
| T3 | (c≥1 and a≥1 and total ≥3) or (a=2 and c=0) | Partial accrual on real revenue, or large revenue bypassing the token |
| T4 | c=1, a≤1, and a chain or total ≤3 | Mechanism present, fuel absent |
| T5 | anything else | Accrual absent or token |
Plus two rules: a reflexive asset cannot reach T1 (demoted to T2); wrappers, unmeasurable and disputed assets receive no tier at all.
The a=2 and c=0 → T3 branch deserves attention. An asset with large revenue that does not reach the token (a governance_shell) ranks above an asset with no revenue at all. The logic: the business exists and earns; turning on a fee switch is a governance decision. Building a working business is harder than deciding to share.
One model spans tiers, and that is correct. buyback_burn is T1 at PancakeSwap, T2 at PUMP (reflexivity), T3 at Clearpool (thin revenue). Same mechanism, different execution.
A curated layer outside the code catches what sources cannot see. Each flag carries a written reason.
| flag | effect | purpose |
|---|---|---|
wash-volume | A ≤ 1 and C ≤ 1 | Volume is fabricated, so both the revenue and the buyback funded by it are unreal |
burn-no-fuel | C = 0 | Burning without activity is not accrual |
no-accrual | C = 0 | Value structurally bypasses the token |
reflexive-cyclical | sets reflexivity, A ≤ 1 | Revenue driven by a speculative loop |
accrual-pending | C ≤ 1 | Mechanism written, flow not started |
post-crash, manipulation-flag, mm-incident | → manual review | Data untrustworthy |
dune-burn-candidate | changes nothing | Marks a treasury-like burn profile worth reviewing by hand. Does not affect the verdict — see §12.7a |
One case is not "no data" but "data cannot exist". An exchange token earns its revenue off-chain, and aggregators will never see it. Refusing to judge such assets forever would leave the method silent about a visible part of the market.
So the mark layer allows a manual verdict: a human sets the axis scores and attaches a written justification. The constraints are strict:
The line runs along the nature of the evidence. An issuer's public buyback report is a statement, not a measurement, so it may name the mechanism but not set the axes. That is how LEO, BGB and HTX are handled: the model is stated, the tier is absent.
A classifier must distinguish "bad" from "unknown". Merging them produces a tool that lies with confidence. Hence a status on every asset:
| status | meaning |
|---|---|
| measured | A verdict from data |
| unmeasurable | No protocol or revenue in the sources → no tier is assigned |
| out of method | A wrapper |
| needs review | Signals conflict, or the text could not be read |
Plus a confidence: high (mechanism read from the description, all three axes from data), medium (mechanism from a language model, or an axis from a fallback), low (goes to review).
The method's rule: no data → a mark, not a guess.
An asset without data never receives a tier.
Sometimes the mechanism is known while business data is absent. A token with a hard supply cap and no revenue capture is monetary by definition; a project's documentation saying plainly "we buy back and burn" names the mechanism whether or not we know the revenue.
In such cases the method names the model and assigns no tier. The model answers "by what mechanism", and text can answer that. The tier answers "how well", and that requires numbers on axes A and C, which are missing. Showing the model is useful; deriving quality from it would be invention.
This follows from the rule about the boundary of knowledge rather than excepting it.
A protocol missing from the sources means "we cannot see the business", not "there is no business". The distinction matters for exchange tokens: an exchange earns its revenue off-chain, where aggregators cannot see it by construction. Labelling such a token monetary would be wrong — Bitget (BGB) runs a working buyback-and-burn funded by off-chain revenue. So for exchange tokens the inference "scarcity bet from a supply cap" is forbidden: they stay unmeasured with the reason stated explicitly.
| status | count | share |
|---|---|---|
| measured | 440 | 44.0% |
| out of method (wrappers) | 125 | 12.5% |
| unmeasurable | 434 | 43.4% |
| needs review | 1 | 0.1% |
Tiers: T1: 12, T2: 9, T3: 27, T4: 168, T5: 220.
Two figures worth discussing with students:
Every run is stored as a snapshot, and verdicts from past dates are never rewritten. That gives two things a one-off assessment cannot.
What changed, and why. A tier change explains nothing by itself: "the asset became T1" may mean revenue grew, a broken join to the source got fixed, or the mechanism was re-read. So the reason is recorded alongside the fact — which axes moved, whether the model changed, how much revenue grew, whether coverage shifted. A score whose origin is invisible does not deserve trust.
What did NOT change. Stability of a verdict across snapshots is information in itself: a protocol's mechanism is stable for months while the figures move daily.
By mechanism, not by sector. Sector membership predicts nothing about accrual. Two decentralised exchanges can be buyback_burn and governance_shell — opposite assets to a holder, identical by sector.
Three axes, not one score. Collapsing to one number destroys the diagnosis. "Earns but does not share" (a=2, c=0) and "shares all it has, which is little" (a=0, c=2) can sum alike while being opposite in nature.
monetary as a full type. Several of the largest assets capture no revenue by design. Without a type for them they would be labelled "poor accrual", which misdescribes them: their thesis is scarcity, and the method must name that rather than penalise it.
governance_shell separated out. It forces a check of who receives the flow.
Wrappers outside tiers. A one-to-one tracker has no accrual of its own.
Reflexivity as a flag. It is perpendicular to mechanism (§4).
The tier computed by code. Identical inputs must yield an identical tier in any run. A language model would drift between runs, and the ranking would move without any change in the facts.
The first version of axis B scored issuance against absolute thresholds: inflation above 8% scored zero, FDV overhang above three scored zero. That construction had three defects, all now removed.
The threshold came from nowhere. Neither 8% nor 3% was derived from anything. The first measurement showed real inflation is bimodal: a dense cluster below ~6% and outliers past 36%. The 8–20% band was empty — the threshold stood in a void and could equally have been 10 or 20. A ratio removes the question: it requires no level, it compares issuance against what opposes it.
A level did not answer the holder's question. A holder is diluted not by the percentage printed but by how much of that printing goes uncompensated. Canton at 9% with ×1.14 coverage and Canton at 3% with zero payouts are opposite situations that an absolute threshold placed side by side.
The axis penalised what had not happened. FDV overhang and pending unlocks are a threat, not dilution. Moving them out of scoring into a separate indicator made the axis measure fact while keeping the threat visible. A realised unlock loses nothing by this: it enters the circulation measurement on its own.
A side gain: refreshing the data acquired a purpose. Previously a re-run changed only revenue figures. Now actual inflation and coverage depend on it, and through them the axes and the tier. An unlock, a change in buyback policy, a revenue decline move the ranking directly.
| approach | what it classifies | why it does not answer our question |
|---|---|---|
| Sector categories (CoinGecko, CMC, Messari) | what the project does | says nothing about accrual; two assets in one sector can be opposites |
| Metric dashboards (Token Terminal, DefiLlama: fees, revenue, P/S, P/E) | how much the protocol earns | they measure flow without classifying mechanism. P/E presumes earnings reach the holder — false for governance_shell. We use them as a data source |
| The "real yield" label (since 2022) | whether a protocol pays "real" yield | binary and coarse: merges buyback_burn with direct_yield, ignores issuance and reflexivity |
| Legal classifications (Howey, MiCA) | legal status | perpendicular to the economics |
| Staking yield dashboards (StakingRewards) | the APR | they do not separate the source — printing versus revenue. That is precisely the inflation_staking / direct_yield boundary, and it is erased there |
| Value-capture essays (fat protocol thesis, Multicoin, a16z) | how tokens capture value in principle | qualitative, without computable thresholds or a "no data" status; not reproducible |
| This method | the mechanism by which value reaches the holder | computable, reproducible, axes separated, the boundary of knowledge stated |
The listed approaches answer adjacent questions — what the project does, how much it earns, its legal status, its APR. This scheme answers one: where the money goes — and carries it to a procedure that can be computed.
buyback_burn can be T1 or T3.buyback_to_holders merges two things. "Distributed to stakers" and "parked in the treasury" differ: the second is not a distribution.A check across 154 addresses showed the size of the error: one token's "burn" came to 2178% of its own market capitalisation in a year, another's to 1106%. Returning eleven times your own value is impossible; this is mint-and-redeem mechanics. A burn above roughly 100% of market cap is a reliable sign of it.
Separating the two by the number of burning addresses (a treasury is a handful, users are thousands) works only partly: when a vault contract redeems on users' behalf, the address count is one as well. So on-chain measurement serves as a shortlist for manual review, not as a source for axis C.
Three statements:
The lesson for a student: twelve assets out of a thousand reach the top tier. A protocol that genuinely earns, does not dilute its holder, and shares with him is rare on this market. Everything else approximates that pattern to varying degrees, and the method exists precisely to show which element is missing.
This document describes the method implemented in accrual_scanner. Thresholds live in config.py::THRESHOLDS, the taxonomy in models.py::Model, the tier formula in classify/tier.py, the correction layer in overrides.yaml.