Start here
The Graduation Score
A weighted composite of five sub-signals, recomputed every block, with one hard override that outranks the weighting.
The score runs from 0 to 100. It is not a snapshot: the pipeline re-scores every tracked token as each block lands, so the useful object is the shape of the line, not the current value. A token holding 74 for six hours and a token that was 88 an hour ago are not the same token.
The five sub-signals
These weights are rendered from the same constants the scoring code uses, so this table cannot drift away from the implementation.
| Signal | Weight | What it measures |
|---|---|---|
| Contract safety | 30% | We try buying and selling it for real, in a test that costs nothing. If the sell fails, it is a honeypot. We also look for hidden mint, blacklists and tax switches in the code. |
| Deployer reputation | 25% | Who made this token, and where their money came from. A fresh wallet funded by last week's rug is not a new person. |
| Distribution & bundles | 20% | Who holds the coins. We flag wallets that bought in the first seconds — those were waiting for it — and how much the biggest ten hold. |
| Liquidity | 15% | How much money is in the pool, and whether anyone can take it out. Burned liquidity cannot be pulled by anybody. |
| Organic momentum | 10% | Is anyone actually buying. We count real buyers, not volume — one wallet trading with itself makes a chart but not demand. |
Calibration
These are starting weights, not a fitted model. They encode a prior — that what the contract can do to you matters more than how fast it is moving — and they are meant to be re-fitted against real outcomes once enough tokens have either graduated or died to train on. Treat the current numbers as a considered guess.
The refit itself is written and runs as npm run calibrate. It reads the closed outcomes the indexer has accumulated, fits the five coefficients by gradient descent and prints the shipped weights against the fitted ones with the ROC AUC of each, so swapping them is a measurement rather than an opinion. Two rules it enforces: the fit is class-weighted, because graduations run near one percent of tokens and an unweighted objective is minimised by answering “no” to everything; and a sub-signal whose fitted coefficient comes out negative is reported and given zero weight rather than folded in, because a negative weight would score a token higher for failing a check — a bug in that sub-signal, not a finding about the market. It refuses to fit under fifty positives.
Coverage
Not every axis can be read on every deployment or for every token. A chain with no factory configured has no locatable pool; a source that cannot name a contract's deployer cannot start a funding trail; a token too busy to replay from its own transfer log has no reconstructable holder set.
When that happens the axis is left out of the composite and named, and the remaining weights are renormalised. Scoring an unreadable axis as zero would turn a missing measurement into a finding: a token whose pool cannot be found would score badly for it, which is a statement about the configuration, not about the token. Scoring it as fifty would be worse — an invented number, indistinguishable from a measured one.
Below half the total weight there is no score at all. Two axes out of five is not a Graduation Score with an asterisk, and a number computed from a third of its inputs would be the most damaging thing this could ship. The screen reports what it could read and what it could not, instead.
The hard override
Weighting is the wrong tool for a disqualifying fact. If the simulated sell reverts against current state, the token is a honeypot and its score is capped at 12 no matter how good the other four signals look:
const weighted = SIGNALS.reduce(
(total, signal) => total + signals[signal.key] * signal.weight,
0,
);
if (sellSimulationFailed) return Math.min(score, 12);
return score;Deep liquidity and roaring momentum on an unsellable token are not mitigating factors — they are the trap working as designed.
Verdict bands
| Score | Verdict | Reading |
|---|---|---|
80–100 | Graduating | No disqualifying contract finding, a deployer with history, supply mostly in organic hands. |
50–79 | Watch | Something is unresolved — usually retained ownership, a mutable tax, or a deployer with no track record. |
0–49 | Avoid | At least one finding that would cost you the position, not just some of it. |
A band is a summary, not a recommendation. The token page always shows which of the five axes produced the number.
What a score is not
- It is not a price prediction. A 91 can still go to zero on a market move that has nothing to do with the contract.
- It is not advice. It is a compression of observable on-chain facts.
- It is not static. Re-read it before acting on it — that is the whole premise.