the exit radar for pons v2 on Robinhood Chain local · open · read-only by default · no key required to use any of it
Everyone is building the thing that tells you when to get in.
Nobody is building the thing that tells you when to leave, because the entry is the part that feels like skill and the exit is the part that feels like luck. It isn't luck. Almost every position that goes to zero announces itself first, in the ledger, several minutes before it announces itself in the price — the creator moves the bag to three fresh wallets, the twenty-eight "holders" that share one funder start unwinding, the liquidity walks out while the candle stays flat, the tax quietly goes from 1% to 12%.
tocsin watches for that. It reads the wallets, not the chart. When something
changes it rings, and it prints the sentence that made it ring.
A tocsin is the bell you ring when the town is on fire.
20 seconds of it running — left window is tocsin scan
walking the chain, right window is tocsin watch on the positions you hold.
Both panes are what the program printed; demo/render.py parses the same ANSI
bytes your terminal gets and lays them on a grid. npm run record regenerates
the whole clip from scratch.
It takes a token you hold, rebuilds its entire transfer history into a ledger, and asks about twenty questions of that ledger every few seconds:
| it asks | because |
|---|---|
| has the creator sold, and in what shape | a drip, a split and a dump are three different futures |
| did the creator sell, or did the wallet the creator funded sell | "dev still holds 100%" is technically true far more often than it is useful |
| how many of these holders are one person | thirty wallets, one funder, one decision |
| is concentration rising right now | someone is accumulating quietly, and it is not you |
| how much of this volume is round-trip | four addresses passing a bag is not a market |
| are distinct buyers arriving or drying up | the tape empties before the price does |
| how much liquidity is left, and whose is it | 90% of the floor being one position owned by the creator is not a floor |
| what is this actually priced in | some of these are quoted in NVDA and SPCX, not ETH |
| how big is your position against real depth | past a certain size, you are the exit |
| did the tax change after you were already in | it does, and nothing tells you |
Then it adds those up into one number from 0 to 100, rings one of four bells, and prints every point that moved the number.
13:52:41 EXIT $GOLDCARD danger 98 · 6 wallets funded by the creator sold 13.55% of supply
13:52:41 Gold Card $GOLDCARD 0x62f2a1…04e3ba ALARM 98
creator 0x7a91…4c2f holds 4.1% (4.10M GOLDCARD) sold 0% tax 1.0% fees → third party
top10 61.2% +5.3pp holders 218 -14 2 clusters 23.4%
flow 0.31 buyers 3/10m -19 sellers 41 round-trip 12%
book ██░░░░░░░░░░░░░░░░░░ 6.20 ETH to −30% fdv 88.40 ETH liq -41% /15m quote ETH
+30 6 wallets funded by the creator sold 13.55% of supply (0x9cd6…486b)
+26 41% of pool liquidity pulled in 15m · +23 2 clusters hold 23.42% — largest is 19 wallets, same funder 0xbab4…
+20 buy/sell pressure flipped 2.14 → 0.31 in 10m · +12 fees route to a third party — builder / KOL deal pattern
-10 creator has not moved a token in 3h9m
The last line is the point. Every number tocsin shows you can be argued with, because tocsin shows you which sentence produced it.
git clone https://github.com/SpikeCalls/tocsin
cd tocsin
npm install
npm run demo # a generated chain, scored by the real engine. no RPC, no keys.
node bin/tocsin.js rules # every rule, its id, what it looks at
node bin/tocsin.js replay farm # score one known shape and show the workdemo is not a video and not a mock-up. It generates transfer logs, funder
maps and liquidity samples, and then feeds them to the same ledger, the same
clustering, the same rules and the same renderer the live command uses. Every
score in it was computed. That is also why the demo doubles as the test fixture
— see test/engine.test.js.
Then, when you want it pointed at the real chain:
cp .env.example .env # fill in RPC + the four addresses
node bin/tocsin.js doctor
node bin/tocsin.js add 0xYourToken --size 41000000
node bin/tocsin.js watchclone it — the way I would do it, because you should read src/score/rules.js
before you trust any of this:
git clone https://github.com/SpikeCalls/tocsin && cd tocsin && npm installor globally, if you already read it:
npm install -g github:SpikeCalls/tocsin
tocsin demoRequirements: Node ≥ 20. Three dependencies: viem, commander, ws. No
build step, no bundler, no telemetry, no account. Nothing leaves your machine
except JSON-RPC to the endpoint you put in .env.
.env.example ships with PONS_FACTORY, PONS_ROUTER, V4_POOL_MANAGER and
V4_STATE_VIEW empty, and that is deliberate.
I am not going to hardcode a protocol address you did not verify and then have
you point a sell router at it because a README told you to. Get them from the
protocol's own docs or from a launch transaction in the explorer, put them in
.env, and then run:
node bin/tocsin.js doctorwhich checks the endpoint answers, that each address actually has code, and
that the ABI in src/chain/abi.js matches what is deployed. If doctor is
unhappy, everything downstream of it is fiction.
Everything except live reads works with none of this set. demo, replay,
rules and the whole test suite run on a fresh clone.
| command | what it does |
|---|---|
tocsin watch |
score every position you hold, forever, and ring when one turns |
tocsin scan |
rank every graduated token on the chain by how badly it wants you gone |
tocsin dossier <token> |
everything it knows about one token, printed long, including every cluster and every rule that fired |
tocsin guard |
watch, and act. dry run unless you pass --live, which asks again in words |
tocsin board |
the same data in a browser, served from 127.0.0.1 only |
tocsin demo |
the generated chain, scored by the real engine |
tocsin replay <shape> |
clean drip split farm drain wash taxraise — score a known shape and show the work |
tocsin rules |
every rule, its id, and what it looks at |
tocsin doctor |
check the endpoint, the addresses, and the ABI |
tocsin add / rm / ls |
the watchlist, which is a flat JSON file you can edit by hand |
The narrow column is the one you leave open. It prints one line per position
per pass and most of those lines say hold, which is the job — a radar that
alarms on everything is a radar you learn to ignore. Anything above calm also
gets a full card in the wide window.
Four bells:
| bell | score | what it means |
|---|---|---|
CALM |
0–24 | nothing in the ledger is asking you to leave |
STIR |
25–49 | one thing changed. worth knowing, not worth acting on |
TOLL |
50–74 | trim. more than one thing is pointing the same way |
ALARM |
75–100 | the shape of an exit is fully formed |
The only part of this program that can spend anything, and it is built to be hard to use by accident. Five gates, all of which must open:
--liveon the command line. Never from.env, never a default.PRIVATE_KEYexists and derives the address that holds the position.- The bell is at or above
GUARD_MIN_LEVEL(defaultalarm). - The tranche fits inside
GUARD_MAX_DEPTH_FRACTIONof real measured book depth — default 8%. If it doesn't fit it is trimmed, and the trim is printed, not hidden. - The session budget has not been spent.
Dry run is not a mode you opt into. It is the mode you have to opt out of, in writing, by typing a word into a prompt. And the dry run exercises the same code path the live run does, which is the entire reason to have one.
Live sending is intentionally not wired to a router in this build. src/guard/sell.js
is where you point it at yours, and that is a decision you should make with your
eyes open rather than inherit from a stranger's default.
┌──────────────────────────────────────────┐
JSON-RPC ───► │ src/chain/client.js │ chunked getLogs,
│ polite queue, backoff, gap tracking │ multicall, no
└───────────────────┬──────────────────────┘ silent truncation
▼
┌──────────────────────────────────────────┐
│ src/read/live.js → snapshot │ ← src/demo/world.js
└───────────────────┬──────────────────────┘ produces the
▼ identical shape
┌──────────────────────────────────────────┐
│ src/read/ledger.js │ balances, first-seen,
│ buildLedger() │ per-wallet in/out,
└───────────────────┬──────────────────────┘ buy/sell events
▼
┌────────────┬───────────────┼───────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
clusters.js creator.js flow.js liquidity.js fees.js
union-find shape + hop wash + tape depth + LP route + change
└────────────┴───────────────┼───────────────┴──────────────┘
▼
┌──────────────────────────────────────────┐
│ src/score/rules.js → engine.js │ points + a sentence
└───────────────────┬──────────────────────┘ per rule
▼
┌──────────────────────────────────────────┐
│ src/view.js → one shape │
└───┬───────────┬────────────┬─────────────┘
▼ ▼ ▼
terminal board guard plan
There is one place that opens a socket, one place that folds transfers into balances, one place that decides what is dangerous, and one shape that every surface renders. The terminal, the browser dashboard and the sell plan all read the same object; there is no second version of the truth that only the dashboard sees.
Two design decisions worth defending:
The score has no memory of price. Not one rule reads a candle. This is not purity, it is that price is the last thing to know and I wanted the earliest thing.
The demo is the fixture. src/demo/world.js fakes the chain, not the
output. If I had faked the output, the screenshots would look great and the
code would be untested, which is the normal arrangement and the reason most of
these tools do not survive contact with a real launch.
npm test30 tests, no network, no mocking framework, run in about half a second. They cover the parts where being wrong is expensive:
- the ledger, including the bug where a mint counted as a sell by the zero address
- clustering, including the case where a dust transfer must not merge two people
- flow, including a ring that round-trips one bag and a window with nothing in it
- the engine's determinism, and its behaviour when a rule is handed a missing field
- all five of guard's gates, including the depth cap and the session budget
LIMITATIONS.md — read it before you rely on any of this.
Short version: funder attribution is a proxy and can be fooled by a CEX
withdrawal; timestamps between two anchor blocks are interpolated; a public RPC
that truncates a log range makes a score partial and tocsin says partial but
cannot conjure the missing data; a creator who never touches the token again
and instead sells the LP position looks quiet to the creator rules; nothing
here sees the mempool, so a sell that lands in the same block as the alarm is a
sell you did not beat; and a token can be perfectly clean by every rule in this
repo and still go to zero because nobody wanted it.
A tool that hides its blind spots is the one that costs you money.
Does it need my private key? No. Every read command works with an address
or nothing at all. Only guard --live touches a key, and it will not arm
without you typing a word into a prompt.
Does anything leave my machine? JSON-RPC to the endpoint in your .env.
That is the entire outbound surface. board binds to 127.0.0.1, not your LAN.
Can I run it on a token I don't hold? Yes — tocsin dossier <token>. The
depth rule is the only one that needs a position size, and it just stays quiet.
Will it front-run the rug? Sometimes, and you should assume not. It sees what is confirmed on chain. It does not see the mempool and it will not race anybody. What it reliably does is catch the setup: the split, the farm unwinding, the liquidity walking out — which usually happens minutes to hours before the last candle, not milliseconds.
Why is it so opinionated about units? Because "$4.2M FDV" on a book quoted in a thin equity token is a sentence that has cost people real money. Every figure here carries the asset it was measured in.
Rules are the interesting surface. A rule is thirty lines in
src/score/rules.js, it takes a context and returns points plus a sentence, and
it needs a test in test/engine.test.js that drives it from a shape in
src/demo/world.js. If you have a pattern you have watched cost you money, that
is exactly the thing worth adding.
Please do not send a PR that adds a price feed to the scoring path.
demo/avatar.py draws it. Rectangles on a 29x29 board, no anti-aliasing, no
gradients, no vector file to lose — which is also why it survives being scaled
down to a 40px avatar. Three other drafts are in docs/ if you prefer a bell,
a watchdog or a goose.
python3 demo/avatar.py --out docs/ --only owl # the mascot
python3 demo/profile.py --out docs/ # the banner and the bell stripTwo enormous eyes and nothing else worth mentioning, which is a fair description of what this program does all day.
MIT. It runs on your machine, not mine.