Rugs
How the night's three rugs and the earlier two could have been refused before the buy, what is live now, and the ceiling sweep.
REPORT-rugs.md · updated 06/09/2026 07:46:31Worker: analysis-rugs. Brief: analysis-2026-09-06/BRIEF-rugs.md. Written 2026-09-06 07:46 Beirut. All times UTC unless marked +03.
TLDR
- The deployer deny list Thomas asked for would not have worked. ZEP, PEZZED and PEZ were deployed by three different fresh EOAs (
0x7d1bcd…,0x62443b…,0x30a3a6…), each a direct contract-creation, each with 7 to 8 lifetime transactions. Banning ZEP's deployer at 03:25 +03 would have stopped nothing at 04:23 or 05:06. - The signalling wallet was the common thread, and we already had its rap sheet.
data/watchtower/smartmoney.jsoncarriedfrankdegods: entries 39, rugs 6(15.4%) withlastTs 2026-09-06T00:00:22Z, twenty-five minutes before the ZEP signal.research/smartmoney/FOLLOW-LIST.jsongraded itverdict: "watch". Nothing inexecutor/follower.mjs filterSignal()reads either field. That is the single cheapest fix on the table: zero RPC calls, one config key. - Four independent pre-buy tells each catch all three of the night's rugs with zero false positives across 36 controls on the debrief tape: pool quote asset is WETH not native ETH; liq/mcap >= 0.85 at signal time at any age; pre-entry sells/buys under 0.30; token bytecode is a hand-written 8.7 KB ERC20 with
owner+maxTxinstead of the 3248 / 181 / 44 byte launchpad shapes. Scorecard in finding 3. - These were honeypots, not only LP pulls. At 01:35 on PEZZED and 02:13 on PEZ the follower fired a real
clip_targetat about +60% and the sell revertedTransferHelper: TRANSFER_FROM_FAILED, once explicitly"Blocked: cannot sell"(data/executor/follower.log). The LP drain came after. No exit rule can beat a contract that refuses to sell, so the money is won or lost at the pre-buy gate. - The existing rugRatio guard already worked, it was just scoped too narrowly. It refused PEZZEL (0.93 at 10 min) and PENZ (0.94 at 9 min) that same night. ZEP (0.895 at 12 min), PEZZED (0.862 at 14 min) and PEZ (0.873 at 16 min) slipped through only because
underAgeMin: 10switches the guard off past ten minutes. - The in-position layer cannot save a hard pull, it can only stop the lying. ZEP's pool went dead about 7 minutes after our fill and we marked it at +18% off dexscreener for 2 h 20 m. A PoolManager liquidity watch buys truth and a fast write-off, not money.
- Sample honesty: five rugs total (ZEP, PEZZED, PEZ, SEPE, CYBERDOG), three of them one family from one wallet in three hours. Every "zero false positives" below is against 36 controls on one tape. These are strong candidates, not laws.
1. Deployer genealogy
Method: Blockscout api/v2 and api are both Cloudflare-challenged from this box right now (HTTP 403, verified twice on both endpoints at 07:36 and 07:38 +03), so the address-history route in the brief was unavailable. Instead each token's first Transfer from the zero address was located with eth_getLogs on the public RPC and the minting transaction's from and to read back. Note for anyone reusing this: the Transfer topic on Robinhood Chain is 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, not the canonical …b68c….
| token | outcome | mint block | deployer EOA | mint tx to |
deployer nonce now | deployer bal now |
|---|---|---|---|---|---|---|
| ZEP | rug | 55540398 | 0x7d1bcd19f1b9900a0ba21f8aba069982dfcc5ddd |
null (raw creation) | 7 | 0.00040 ETH |
| PEZZED | rug | 55573589 | 0x62443ba303206ccbb1a0cefdd9315c08e46f3913 |
null (raw creation) | 7 | 0.00041 ETH |
| PEZ | rug | 55598049 | 0x30a3a65bb0e022530aad7fe3e874f8bf4743c2e3 |
null (raw creation) | 8 | 0.00003 ETH |
| SEPE | rug | 54746143 | 0xeae350215fb186ddf26d280bb713b34c2a348ec9 |
0xefd94948… (factory) |
30 | 0.00001 ETH |
| CYBERDOG | rug | 54363123 | 0xa8189779f819b34cf75153cce2d400ff7c0c45a5 |
0xdb3d02bf… (factory) |
41 | 0.00001 ETH |
| LIQLORACLE | +551% | 55563380 | 0xabe3de2c52bd3ebe03aaa0a641872adc4a38b78a |
0x22e99278… (factory) |
20 | 0.128 ETH |
| hoodrat | +65% | 55376942 | 0xe2f5cae53f2e23651d8ff0ae434ea21b386c3624 |
0xb2a748f6… (factory) |
4 | 0.408 ETH |
| BUSINESS | +17% | 55433560 | 0x2857bbe641c37fa9f5ad9cfde029fd04b92a9371 |
0x22e99278… (factory) |
28 | 0.00305 ETH |
Source: analysis-2026-09-06/data/rugs/mints-out.txt, deployer-nonces.txt.
Reads:
- Three rugs, three deployers, zero overlap. A deny list keyed on the deployer has no predictive value inside a night. It has value only across nights, and only if we can enumerate a deployer's earlier launches, which needs an indexer we do not currently have.
- Raw contract creation is the family signature. The three frankdegods-family tokens were deployed straight from an EOA (
to: null). Every control checked went through a factory. That fires on 3/3 of the family and 0/5 controls checked, but the sample of controls with resolved mints is only five, so treat it as a candidate, not a scored tell. - The funding source question is unanswered. The public RPC has no archive state (
eth_getTransactionCountat a historical block returnsmetadata is not found), and Blockscout is blocked, so "who funded these three burners" could not be established. This is the one item in the brief I could not deliver. It goes in needs Thomas below. - The signalling wallet
0x696d…is not an EOA in the plain sense.eth_getCodereturns0xef0100e6cae83bde06e4c305530e199d7217f42808555b, an EIP-7702 delegation designator pointing at0xe6cae83b…. frankdegods trades through a delegated smart account, which is consistent with an automated launch-and-buy loop rather than a human clicking. - The wallet history was on disk.
data/watchtower/smartmoney.jsonwallet row for frankdegods atlastTs 2026-09-06T00:00:22.438Z:entries 39, rugs 6, rank 49, pnl7d 880184. The rug count is computed inwatchtower/smartmoney.mjs:183as entries that either rugged or printedminPct <= -80. It is refreshed continuously and it is the same number the debriefs quote. The three lots of the night are not in that 6, so 15.4% was the prior rate.
Rug rate over the whole tracked cohort, from the same file (analysis-2026-09-06/data/rugs/smartmoney-rugs.txt, wallets with 5+ entries):
| handle | entries | rugs | rate | what it gave us on this tape |
|---|---|---|---|---|
| loganlim_x | 5 | 2 | 0.400 | nothing followed |
| seralberttrades | 11 | 3 | 0.273 | AJC -63%, PETOI -71%, Uranus -22% |
| Rowdy | 25 | 6 | 0.240 | nothing followed |
| fr3ak | 12 | 2 | 0.167 | hoodrat +65%, MADETEST -54%, MUPPETS -49%, Culio -98% |
| frankdegods | 39 | 6 | 0.154 | ZEP, PEZZED, PEZ (-$150), RF +194% (Wide set) |
| iruletrenches | 10 | 0 | 0.000 | BODKIN +196% |
| NotARandomUser | 9 | 0 | 0.000 | DOOKIE -58% |
| picadura | 7 | 0 | 0.000 | LIQLORACLE +551% paper, +$128.86 live |
A maxWalletRugRate gate at 0.15 refuses frankdegods, seralberttrades, fr3ak, Rowdy, loganlim_x, Binkieee, elliotrades and about fifteen more. On this tape that costs hoodrat (+65%) and RF (+194%, Wide set, not live money) and saves the three rugs plus AJC, PETOI, Uranus, MADETEST, MUPPETS, Culio. On live Gold v2 dollars it is clearly positive; on paper Wide it is not.
2. Pre-buy tells, scored
Every tell below was computed from what was on disk at or before our entry timestamp: data/watchtower/feed.jsonl fields at signal time, and debriefs/<call>/swaps.jsonl truncated at metrics.json entryTs. 41 debriefed calls, 5 of them rugs. Full table: analysis-2026-09-06/data/rugs/tell-scorecard.txt.
| tell | fires on | rugs caught | false positives |
|---|---|---|---|
| pre-entry sells/buys < 0.30, at least 30 pre-entry swaps | 3 | 3/5 | 0 |
pool quote asset is WETH (0x0bd7d308f8…) not native ETH |
3 | 3/5 | 0 |
| liq/mcap >= 0.85 at signal, any age | 3 | 3/5 | 0 |
| liq/mcap >= 0.80 at signal, any age | 3 | 3/5 | 0 |
bytecode has owner() + maxTxAmount() selectors |
3 | 3/4 scanned | 0 |
bytecode has owner() alone |
4 | 3/4 scanned | 1 (Viagra) |
The three that all four tells catch are ZEP, PEZZED and PEZ. None of the four catches SEPE or CYBERDOG, which were 181-byte launchpad clones on native-ETH pools with ordinary two-sided flow. SEPE has its own tell and it is already shipped (liq/mcap 1.599 at 3 minutes).
2a. Pre-entry sell flow is the sharpest number on the tape
| token | pre-entry swaps | buys | sells | sells/buys |
|---|---|---|---|---|
| PEZ | 74 | 70 | 4 | 0.057 |
| PEZZED | 100 | 91 | 9 | 0.099 |
| ZEP | 129 | 117 | 12 | 0.103 |
| lowest control: MILADY | 885 | 505 | 380 | 0.752 |
| next: BBM | 507 | 289 | 218 | 0.754 |
| next: ELFIE | 3906 | 2225 | 1681 | 0.756 |
| median of 36 controls | ~0.95 |
The gap is 7x, not marginal. Thomas's note ("163 buys / 4 sells in h1 on PEZ") generalises: on a real launch roughly as many wallets sell as buy in the first minutes; on this family almost nobody sold, because almost nobody could. A threshold anywhere between 0.25 and 0.60 separates them cleanly on this tape. 0.30 with a 30-swap minimum is the conservative pick.
Second-order signal in the same numbers: the rugs are thin. 74 to 129 pre-entry swaps against 900 to 14,000 on the controls. Absolute swap count alone is not clean enough to use (Launch had 23, TERROR 8, SNOO 75, all honest), but combined with the sell ratio it is the same fingerprint.
2b. Liquidity to market cap, freed from the ten-minute window
The guard exists (executor/follower.json filters.rugRatio: { maxLiqToMcap: 0.8, underAgeMin: 10 }) and it worked. From data/watchtower/feed.jsonl:
| token | age at signal | liq/mcap | verdict that night |
|---|---|---|---|
| PENZ | 9 min | 0.941 | refused, "liquidity is 0.94x the market cap on a 9m old pool" |
| PEZZEL | 10 min | 0.929 | refused, "liquidity is 0.93x the market cap on a 10m old pool" |
| ZEP | 12 min | 0.895 | bought, guard off past 10 min |
| PEZ | 16 min | 0.873 | bought, guard off past 10 min |
| PEZZED | 14 min | 0.862 | bought, guard off past 10 min |
| LIQLORACLE (+551%) | 7 min | 0.754 | bought, correct |
| BUSINESS (+17%) | 6 min | 0.793 | bought, correct |
| AJC | 20 min | 0.331 | bought |
| hoodrat (+65%) | 4 min | 0.169 | bought |
| MADETEST | 7.5 min | 0.166 | bought |
The whole family sits in a band, 0.86 to 0.94, and the best control on the tape sits at 0.793. Raising underAgeMin from 10 to 60 (i.e. applying the ratio across the follower's entire 2 to 60 minute age window) and holding maxLiqToMcap at 0.80 refuses all five family tokens and nothing else in the feed rows I checked. The nearest control, BUSINESS at 0.793, survives by 0.007, which is uncomfortably tight, so 0.85 is the safer number and still catches all five.
2c. Quote asset and contract shape
Every token on the debrief tape resolves to one of three bytecode shapes (analysis-2026-09-06/data/rugs/bytecode-tells.txt):
| shape | bytes | count | what it is | rugs |
|---|---|---|---|---|
| launchpad token A | 3248 | 26 | standard clone, only burn() exposed |
0 |
| launchpad token B | 44 to 283 | 11 | minimal proxy | SEPE, CYBERDOG |
| hand-written ERC20 | 8722 to 8777 | 3 | owner, renounceOwnership, maxTxAmount, OZ 4.x revert strings, PEZ embeds the string ipez.family |
ZEP, PEZZED, PEZ |
| one-off | 7003 | 1 | Viagra, owner + burn, honest |
0 |
And the quote asset: 16 of the 41 pools quote in native ETH, 22 in assorted quote tokens (USDG, PONS, SLV, ETH-wrappers and others), and exactly three quote in WETH 0x0bd7d308f8e1639fab988df18a8011f41eacad73: ZEP, PEZZED, PEZ. The WETH quote is also why the sell path had no route at all on the night, since the v4 sell leg assumed native-ETH pools until the 05:29 UNWRAP_WETH work. Two problems with one root.
Honest caveat on both: these three tokens came from one operator on one night. "WETH-quoted" and "8.7 KB owner contract" may be that operator's habits rather than a rug law. They are cheap enough to run as soft signals (log and alert) before they become hard refusals.
2d. Tells I could not test
- LP position ownership and locks. Uniswap v4 positions live in the PositionManager keyed by a salt; resolving "who holds the liquidity for pool X" needs either an indexer or a subgraph. Not reachable with Blockscout down. Untested.
- Deployer share of supply, deployer share of first-minute buys, unique buyers, one-funder clusters. All need per-swap sender addresses.
debriefs/*/swaps.jsonlrecordsblk, idx, tx, tok, q, px, buy, qRes, ts, pct, usdand no sender, so none of these could be computed from the tape. This is a concrete gap in what we record, see build. - Hooks. The rugs' pool keys were not resolvable from what is on disk. Note from the night: HOVER was a hooked pool with a dynamic fee and was a false control, so hooks by themselves are not a rug tell.
3. What the follower actually did
From data/executor/follower.log and data/executor/follower-fills.jsonl.
| ZEP | PEZZED | PEZ | |
|---|---|---|---|
| signal | 00:25:46 | 01:23:08 | 02:06:27 |
| live buy | 00:25:57 | 01:23:21 | 02:06:44 |
| first failed exit | 04:55 +03 = 01:55, time_stop |
01:35, clip_target reverted TRANSFER_FROM_FAILED |
02:13, clip_target reverted TRANSFER_FROM_FAILED |
| first "no aggregator route to mark" | 00:33 | 01:38 | 02:24 |
paper book liquidity_pulled |
00:34:17 | 01:38 | 02:25 |
| write-off | 02:54:32 | 02:54:33 | 02:54:37 |
| dead lot carried | 2 h 20 m | 1 h 16 m | 0 h 30 m |
| failed exit attempts logged | 248 | 344 (+27 requotes) | 166 (+87 requotes) |
Four things fall out.
- The
clip_targetreverts are the story. On PEZZED at 01:35 and PEZ at 02:13 the ladder did exactly the right thing, fired a 50% clip at about +60%, and the token refused:TransferHelper: TRANSFER_FROM_FAILED, and once at 01:37 the unambiguous"Blocked: cannot sell". Sells worked earlier (12 sells in ZEP's pre-entry tape, 9 in PEZZED's), so this is a delayed honeypot: the owner flips a flag once enough buyers are in. That also means a pre-buy sell simulation would have passed at signal time and would not have saved these three. Worth building anyway for the static-honeypot case, but do not sell it as the fix for this family. - ZEP's 90-minute time stop was never late. The open question in
memory/2026-09-06.md("why 04:55 and not 01:55") is a timezone artifact: the buy was 00:25:57 UTC = 03:25 +03, and the stop fired at 01:55 UTC = 04:55 +03, exactly 90 minutes later. No ladder bug. Closing that question. - The marking was the real damage. From 00:33 the follower logged
ZEP: no aggregator route to mark …, marking off the dexscreener mid $0.000001094 until one comes backand kept the lot at +18% for 2 h 20 m. The stop loss could never trigger because the mark never went down. The 05:29v4PoolDrainedfix addresses exactly this and would have cut ZEP's dead hold from 140 minutes to about 1 minute. - A liquidity-drop watch would have told the truth, not saved the money. The quote reserve on all three pools never fell in the swap tape: ZEP
qResends at 26.647, which is also its maximum; PEZZED ends at its maximum 34.603; PEZ at 41.499 (analysis-2026-09-06/data/rugs/preentry-tells.txt). An LP burn is not a swap, so it is invisible to the swap feed by construction, and by the timev4PoolLiquidityByIdreads zero there is nothing to sell into. A "liquidity below 50% of entry" watch is worth building for the gradual drain case and for honest marking, but on this family it arrives after the door is shut. Only the pre-buy gate saves money here.
Recommendations
Ship now (config only, reversible)
filters.rugRatio.underAgeMin: 10 -> 60andmaxLiqToMcap: 0.8 -> 0.85inexecutor/follower.json(mirror inwatchtower/rules-v2.jsonGold v2, the two must move together perfixes-2026-09-05/FIELDS.md). Refuses ZEP, PEZZED, PEZ, PEZZEL, PENZ, SEPE. Refuses nothing else in the feed rows checked. Cost: zero RPC, zero latency, the field is already on every feed row. This is the highest value single line in the report.- Extend
filters.handlesDenybeyond frankdegods with the wallets whose prior rug rate is at or above 0.15 and that have signalled us:seralberttrades,fr3ak,Rowdy,loganlim_x,Binkieee. On this tape that gives up hoodrat (+$43.23 realised across two clips and a trail) and blocks AJC (-$26.40), PETOI, Uranus, MADETEST, MUPPETS and Culio. Net positive on live dollars, but it is a static list and it will go stale, so treat it as a bridge to item 4 below. - Leave
burnedWalletHours: 24alone. It is correct and it was shipped at 05:29. Note only that it is a post-loss rule: it cannot fire until one lot has already been written off, and on the night it would have fired only after ZEP's write-off at 02:54, by which point PEZZED and PEZ were already bought. It is a floor, not a fix.
Build (code, with the test that proves it)
walletRugRatefilter inexecutor/follower.mjs. New check insidefilterSignal()(executor/follower.mjs:203), placed next to the existinghandlesDenyblock at line 253. Reads the wallet row out ofdata/watchtower/smartmoney.json(already on disk, already refreshed by the watchtower, no new feed), computesrugs / entries, refuses whenentries >= cfg.filters.minWalletEntriesForRugRate(default 8) and the rate is at or abovecfg.filters.maxWalletRugRate(default 0.15). Refusal string:wallet <handle> has rugged 6 of 39 tracked entries (15%), over the 15% ceiling. Test:executor/test-follower.mjs, new group asserting (a) frankdegods at 6/39 is refused, (b) picadura at 0/7 passes, (c) a wallet with 3 entries and 1 rug passes because it is under the entries floor, (d) a missing smartmoney row passes rather than blocking the book. This is the check that would have refused all three lots at 00:25, 01:23 and 02:06 with no on-chain call and no added latency.sellFlowSkipfilter, same function. Refuse when the pre-entry tape has at least 30 swaps and fewer than 0.30 sells per buy. The counts are not on the feed row today; the collector that writesdata/watchtower/feed.jsonlalready walks the pool's swaps to computerunH1Pct, so addpreBuysandpreSellsthere and read them infilterSignal(). Test:executor/test-follower.mjsfixture rows for PEZ (70/4, refused), ZEP (117/12, refused), MILADY (505/380, passes), Launch (12/11 on 23 swaps, passes because under the swap floor).- Record the swap sender. Add the
sender/originaddress to each row ofdebriefs/*/swaps.jsonland to whatever the collector persists. Without it, deployer-share-of-buys, unique-buyer counts and one-funder clusters, three of the tells the brief asked for, are permanently uncomputable. This is the single biggest data gap in the rug work. - Pre-buy quote-asset and bytecode probe, log-only first. In
executor/routes.mjs,v4PoolKey()(line 259) already resolves the pool's counter currency before every buy, so flagging "counter is WETH, not native" is free. Add oneeth_getCodeon the token (about 40 ms, one Alchemy call) and flagowner()+maxTxAmount()selectors and code length over 8000 bytes. Ship these as alerts, not refusals, for a week, then score them. n=3 does not earn a hard gate. Test:executor/test-univ4.mjs, and note the group-2 CYBERDOG fixture is stale (delisted) and needs replacing regardless. - Liquidity-drop watch in the mark loop.
executor/follower.mjsmark step 3 already callsv4PoolDrainedwhen the simulate fails. Extend it to a ratio: recordentryLiquidityfromroutes.mjs v4PoolLiquidityById(poolId)at fill time, and on every mark cycle exit at market when live liquidity is under 50% of it. Test:executor/test-follower.mjswith a stubbedv4PoolLiquidityByIdreturning entry, 0.6x (hold), 0.4x (exit), 0 (write-off). Be honest about what it buys: on ZEP, PEZZED and PEZ it buys a truthful mark and a 1-minute write-off instead of a 2 h 20 m fiction. It does not buy dollars back. - Automatic deny-list feeding. On every
liquidity_pulledclose, append the signalling wallet and the token's deployer EOA (resolvable with the same mint-log lookup used here, two RPC calls) to adata/executor/rug-wallets.jsonfile thatfilterSignal()reads. Deployer entries will rarely fire twice, per finding 1, but they cost nothing and they are the only way a genuine repeat deployer ever gets caught.
Needs Thomas
- An indexer with address history. Blockscout is Cloudflare-blocked from this box on both
/apiand/api/v2, and the public RPC serves no archive state. Without one of a Blockscout API key, an Alchemy tier withgetAssetTransfers, or another Robinhood Chain indexer, we cannot answer "how many tokens has this EOA launched before" or "who funded this burner", which is the literal wording of the brief. This is also the execution lane's problem, so decide it once. - Accept the cost of the wallet gate. A 0.15 rug-rate ceiling refuses roughly a quarter of the tracked cohort, including fr3ak, which produced hoodrat. Confirm you want that trade before item 4 goes live rather than log-only.
Open questions
- Are the three burner deployers funded from one wallet? Unanswerable here (item 10). If they are, a funder deny list beats a deployer one and would have caught PEZZED and PEZ after ZEP.
- What flipped the sell block, and when?
"Blocked: cannot sell"appears exactly once in the log, at 01:37 on PEZZED, whileTRANSFER_FROM_FAILEDappears 114 times. Reading the ZEP/PEZ contract source (verified or decompiled) would tell us whether the switch is an owner call, a block-height trigger, or a buy-count trigger, and therefore whether any pre-buy simulation could ever have seen it coming. - Is the WETH-quote correlation real or is it one operator's habit? Three tokens, one deployer family, one night. Needs a week of log-only scoring.
- LIQLORACLE at liq/mcap 0.754 was the night's biggest winner (+551% paper, +$128.86 realised live) and sits 0.05 below the proposed 0.80 line and 0.10 below the safer 0.85. That is the false-positive risk in the highest-value recommendation in this report, and it is worth watching that number specifically over the next fifty calls.
- CYBERDOG and SEPE are caught by none of the new tells. Two rugs, launchpad clones, ordinary flow. We do not currently have a pre-buy story for that shape beyond the liq/mcap ratio that already catches SEPE.
Sources
data/executor/follower-fills.jsonl(fieldsside,reason: liquidity_pulled,ts,pnlUsd),data/executor/follower.log,data/executor/follower-state.jsondata/watchtower/feed.jsonl(fieldsliqToMcap,ageMin,liquidityUsd,mcap,runH1Pct,otherCohortWallets10m,verdicts,live.state),data/watchtower/smartmoney.json(wallet rows:entries,rugs,rank,lastTs),data/watchtower/state.jsondebriefs/{ZEP,PEZZED,PEZ,SEPE,CYBERDOG,LIQLORACLE,hoodrat,BODKIN,ETHOSIS,AJC,MADETEST,MUPPETS,BUSINESS,...}-2026-09-0*/{metrics.json,swaps.jsonl,REPORT.md}(41 calls)research/smartmoney/FOLLOW-LIST.json(93 wallets,verdict,wash),watchtower/smartmoney.mjs:183(rug definition),watchtower/callers.mjs:248executor/follower.json,executor/follower.mjs(filterSignal203,rugRatioSkip192,burnedWallet1003),executor/routes.mjs(v4PoolKey259,v4PoolLiquidityById397,v4PoolDrained403),watchtower/watchtower.mjs(PONS56,pollPons485)memory/2026-09-06.md05:29 block- On-chain, this session: public RPC
eth_getLogs/eth_getTransactionByHash/eth_getTransactionCount/eth_getBalance, Alchemyeth_call/eth_getCode. Working files underanalysis-2026-09-06/data/rugs/:feed-cases.txt,metrics-summary.txt,preentry-tells.txt,tell-scorecard.txt,bytecode-tells.txt,honeypot-strings.txt,mints-out.txt,deployer-nonces.txt,smartmoney-rugs.txt,wallet-verdicts.txt, plus the scriptsgenealogy.mjsandmints.mjs.
data/tuning/SWEEP.md · updated 06/09/2026 10:40:55Brief: analysis-2026-09-06/BRIEF-tuning.md. Script: sweep.mjs (rerunnable, node stdlib only, imports
filterSignal / rugRatioSkip / liqToMcap / walletRugRateSkip from executor/follower.mjs). Full grid:
sweep.json (1044 rug-gate points + 6 age points). Disk-only replay, no RPC.
Tape: data/watchtower/feed.jsonl, 1422 rows / 330 tokens, 2026-09-04T22:17Z to 2026-09-06T07:31Z.
0. Headline
| # | Finding |
|---|---|
| 1 | No setting in the grid keeps every rug out. PEZ re-signals at 02:16:54Z at age 26 min with liqToMcap 0.681, from wallets with no rug history. 0 of 1044 grid points refuse it. |
| 2 | The rug-ratio gate does all the work the wallet gate was meant to do. At underAgeMin >= 20 and maxLiqToMcap in {0.80, 0.82, 0.85} it refuses a row of all 6 rug tokens and loses zero runners. |
| 3 | The shipped maxWalletRugRate 0.15 / minWalletEntries 8 adds zero rug refusals on top of that and costs 8 of the 14 runners in the pool. minWalletEntries is the wrong number, not the rate. |
| 4 | maxLiqToMcap has a wide dead zone. Every value in [0.80, 0.85] gives the identical result. The only clean separation on the tape is runner ceiling 0.793 (BUSINESS) to rug floor 0.862 (PEZZED). |
| 5 | The "runner" label is a peak proxy, not money. The 8 runners the shipped gate blocks sum to -50.18 Gold v2 paper and +16.83 live. Losing them is close to free. |
1. Method and universe
Pool. "Rows Gold v2 would otherwise have taken" is built by running the live filterSignal with the three
gates this sweep moves (maxAgeMin, rugRatio, maxWalletRugRate) switched off, and every gate it does not
move left on: band in {under 250k, 250k to 1M}, minAgeMin 2, liquidity 25k-400k, maxMcapUsd with the 2h /
3M fresh exemption, minBuyUsd 100 (measured receipts only), maxOtherCohortWallets 1, maxRunH1Pct 500.
entry.cooldownMinPerToken 120 is applied in time order so a token cannot be counted eight times.
handlesDeny is emptied for the replay, because the static frankdegods entry is exactly the thing the wallet
rule is meant to replace. Two Gold v2 gates are not reconstructible from disk and are not applied:
entry.maxChasePct (needs the live exec quote at signal time) and universe.maxSignalAgeSec (a liveness
check, satisfied by construction when the row was written).
| Set | Rows | Tokens | rug | runner | flat | unknown |
|---|---|---|---|---|---|---|
| Whole feed | 1422 | 330 | 12 | 34 | 56 | 228 |
| Pool (age ceiling 60) | 60 | 41 | 6 | 14 | 8 | 13 |
| Pool (no age ceiling, for the age sweep) | 671 | 153 | - | - | - | - |
Rug tokens in the pool: SEPE, ZEP, PEZZEL, PEZZED, PEZ, PENZ. Runner tokens: AJC, BODKIN, BUSINESS, CLANKSEN, Culio, ETHOSIS, LIQLORACLE, MADETEST, MUPPETS, PETOI, PasDePanique, RF, ROBBING, hoodrat.
No-outcome rows. 13 of 41 pool tokens (17 of 60 pool rows) have no book, no debrief and no fill, so no label. They are counted in every "rows refused" column and excluded from every rate. On the whole feed the figure is 228 of 330 tokens, which is why every rate below is quoted against the pool, not the feed.
Money. Credited once per token, on its first refused row. paperG2 is the Gold v2 book close
(pnlUsd summed over closes); paperAny falls back to the widest v2 book for tokens that predate the Gold v2
set (created 2026-09-05T07:42Z), so it mixes sets and is the weaker column; live sums follower-fills.jsonl
sell and writeoff pnlUsd. Sign convention throughout: negative = refusing that row saved money, positive =
refusing forwent money.
2. The decisive rows
Every rug and every near-line runner in the pool, with the two numbers the gates read:
| Label | Symbol | ts | ageMin | liqToMcap | peak% | handle | paperG2 | live |
|---|---|---|---|---|---|---|---|---|
| rug | SEPE | 09-05 02:01Z | 3 | 1.599 | 3.1 | 6 handles | - | -50.00 |
| rug | ZEP | 09-06 00:25Z | 12 | 0.895 | 18.6 | frankdegods | -50.00 | -50.00 |
| rug | PEZZEL | 09-06 00:46Z | 10 | 0.929 | 81.7 | frankdegods | - | - |
| rug | PEZZED | 09-06 01:23Z | 14 | 0.862 | 70.0 | frankdegods | -11.07 | -50.00 |
| rug | PEZ | 09-06 02:06Z | 16 | 0.873 | 144.2 | frankdegods | -8.64 | -50.00 |
| rug | PEZ (2nd) | 09-06 02:16Z | 26 | 0.681 | 144.2 | AvgJoesCrypto, RugDalio | - | - |
| rug | PENZ | 09-06 02:39Z | 9 | 0.941 | 322.5 | frankdegods | - | - |
| runner | BUSINESS | 09-05 | 6 | 0.793 | 77.0 | lilbro | +8.61 | - |
| runner | LIQLORACLE | 09-05 | 7 | 0.754 | 2296.4 | picadura | +275.67 | +128.86 |
Reading the table: the rug-ratio gate has a clean window of 0.793 to 0.862 and an age window whose binding rug is PEZ at 16 min. Everything else in the grid is noise around those two numbers.
The PEZ hole. At 02:16:54Z the LP had topped the pool up, so PEZ came back at liqToMcap 0.681, ten
minutes after the 02:06 signal, from @AvgJoesCrypto (7 entries, 1 rug, 0.143) and @RugDalio (no rug row in
smartmoney.json at all, so no rate to test). 0.681 is under every maxLiqToMcap in the grid and both wallets
are under every maxWalletRugRate. In the replay that row is always taken, so takenLabels.rug is 1 at all
1044 grid points. Live this did not happen, because the 02:06 buy set the per-token cooldown; in the replay
the 02:06 row is refused, which re-opens 02:16. That is the honest shape of the risk: the ceiling gates buy a
first-signal veto, not immunity, and a rug that reloads its pool walks back in through the front door.
3. Pareto front
Settings that refuse a row of all 6 rug tokens, ordered by runners lost then Gold v2 paper forgone. 772 of the
1044 grid points refuse all 6. To keep every number exact, the table is pinned at underAgeMin 60, the
shipped value (20, 30 and 45 give the same runner and money columns; only the unknown-token counts move by one).
At 60, 157 of the 174 points refuse all 6 and collapse to 35 outcome classes; the front and the notable classes below it are:
| Class | ratio | wrate@min (all equivalent) | Runners lost | Names | flat | unk | rows refused | tokens taken | paperG2 | paperAny | live |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0.80, 0.82, 0.85 | off, 0.18@15, 0.20@15, 0.25@15, 0.30@8, 0.30@10, 0.30@15 | 0 | - | 0 | 4 | 16 | 32 | -69.71 | -15.25 | -200.00 |
| 2 | 0.80, 0.82, 0.85 | 0.30@5 | 0 | - | 0 | 5 | 17 | 32 | -69.71 | -15.25 | -200.00 |
| 3 | 0.80, 0.82, 0.85 | 0.10@15, 0.12@15, 0.15@15 | 1 | RF | 1 | 6 | 20 | 28 | -69.71 | +49.36 | -200.00 |
| 4 | 0.90 | 0.10@15, 0.12@15, 0.15@15 | 1 | RF | 1 | 5 | 19 | 29 | -69.71 | +49.36 | -200.00 |
| 5 | 0.78 | off, 0.18@15, 0.20@15, 0.25@15, 0.30@8+ | 1 | BUSINESS | 0 | 4 | 17 | 31 | -61.10 | +2.77 | -200.00 |
| 7 | 0.80, 0.82, 0.85 | 0.18@10, 0.20@10, 0.25@10 | 2 | AJC, PETOI | 1 | 4 | 19 | 30 | -101.01 | -106.23 | -226.40 |
| 8 | 0.78 | 0.10@15, 0.12@15, 0.15@15 | 2 | BUSINESS, RF | 1 | 6 | 21 | 27 | -61.10 | +67.38 | -200.00 |
| 9 | 0.75 | off, 0.18@15, 0.20@15, 0.25@15, 0.30@8+ | 2 | BUSINESS, LIQLORACLE | 0 | 4 | 19 | 30 | +214.57 | +278.44 | -71.14 |
| 11 | 0.80, 0.82, 0.85 | 0.18@5, 0.20@5, 0.25@5 | 3 | AJC, PETOI, PasDePanique | 1 | 6 | 22 | 30 | -101.01 | -134.45 | -226.40 |
| 12 | 0.80, 0.82, 0.85 | 0.18@8, 0.20@8, 0.25@8 | 3 | AJC, PETOI, PasDePanique | 1 | 5 | 21 | 30 | -101.01 | -134.45 | -226.40 |
| 13 | 0.78 | 0.18@10, 0.20@10, 0.25@10 | 3 | AJC, BUSINESS, PETOI | 1 | 4 | 20 | 29 | -92.40 | -88.21 | -226.40 |
| 17 | 0.75 | 0.18@10, 0.20@10, 0.25@10 | 4 | AJC, BUSINESS, LIQLORACLE, PETOI | 1 | 4 | 22 | 28 | +183.27 | +187.46 | -97.54 |
| 20 | 0.80, 0.82, 0.85 | 0.10@10, 0.12@10, 0.15@10 | 7 | AJC, Culio, MADETEST, MUPPETS, PETOI, RF, hoodrat | 2 | 6 | 27 | 23 | -119.89 | -27.15 | -183.17 |
| 23 | 0.80, 0.82, 0.85 | 0.10@8, 0.12@8, 0.15@8 (the shipped config) | 8 | AJC, Culio, MADETEST, MUPPETS, PETOI, PasDePanique, RF, hoodrat | 2 | 7 | 30 | 22 | -119.89 | -55.37 | -183.17 |
| 33 | 0.75 | 0.10@5, 0.12@5 | 10 | + BUSINESS, LIQLORACLE | 2 | 8 | 35 | 20 | +164.39 | +238.32 | -54.31 |
Class 1 is the tip: it is the only class that refuses every rug token without touching a single runner. Class 7 is the money-best point on live and on Gold v2 paper. The shipped config sits at class 23, eight runners deep into the front, buying nothing over class 1 in rug coverage and taking only 22 of the pool's 41 tokens.
Reference points side by side:
| Config | rugs refused | runners lost | rows refused | tokens taken | paperG2 | live |
|---|---|---|---|---|---|---|
| Baseline before today (10 / 0.80 / no wallet gate) | 3 of 6 | 0 | 12 | 35 | 0.00 | -50.00 |
| Shipped this morning (60 / 0.85 / 0.15@8) | 6 of 6 | 8 | 30 | 22 | -119.89 | -183.17 |
| Ratio only (60 / 0.82 / off) | 6 of 6 | 0 | 16 | 32 | -69.71 | -200.00 |
| Conservative (60 / 0.82 / 0.15@15) | 6 of 6 | 1 (RF) | 20 | 28 | -69.71 | -200.00 |
| Recommended (60 / 0.82 / 0.18@10) | 6 of 6 | 2 (AJC, PETOI) | 19 | 30 | -101.01 | -226.40 |
Every row of this table still takes PEZ once, on its 02:16Z re-signal. See section 2.
4. Recommendation
filters.rugRatio.underAgeMin 60 (keep as shipped)
filters.rugRatio.maxLiqToMcap 0.82 (from 0.85)
filters.maxWalletRugRate 0.18 (from 0.15)
filters.minWalletEntriesForRugRate 10 (from 8)
filters.maxAgeMin 60 (keep)
The age half of the rug-ratio gate is the lever that actually catches rugs and it is free: underAgeMin 10
catches 3 of 6, 15 catches 5 of 6, and 20 catches all 6, after which 30, 45 and 60 are byte-identical because no
runner in the pool has a liqToMcap above 0.793 at any age. Keeping the shipped 60 buys 44 minutes of margin
over PEZ at 16 min for nothing, so keep it. The ratio half is a dead zone: 0.80, 0.82 and 0.85 produce exactly
the same refusals, the same runners and the same dollars, so the choice is pure margin geometry, and 0.82 is the
only value that centres the one clean separation the tape gives us. Nearest miss below the line is BUSINESS at
0.793 (peak 77%, +8.61 Gold v2 paper), 3.3% under 0.82; nearest miss above is PEZZED at 0.862 (-50.00
live), 5.1% over. Shipped 0.85 leaves PEZZED only 1.4% of headroom, and 0.80 leaves BUSINESS only 0.9%; with 6
rugs and 14 runners neither tail is estimated well enough to justify sitting on either edge. Going to 0.78
starts costing BUSINESS, and 0.75 costs LIQLORACLE, the tape's single best name at 0.754 (+275.67 paper,
+128.86 live), which is a $128 mistake to avoid a $50 one. The wallet gate is the one place the shipped numbers
are wrong: at minWalletEntries 8 a 0.15 rate is 1.2 rugs, which is indistinguishable from a clean wallet, and
it costs 8 of the 14 runners for zero additional rug refusals. Moving it to 0.18 at 10 entries blocks exactly
one wallet with pool rows (@seralberttrades, 3 of 3 pool tokens lost money) and hands back @fr3ak, whose pool
contained hoodrat, the day's only live winner besides LIQLORACLE. Note that handlesDeny: ["frankdegods"] stays
in the live config and is doing the frankdegods work regardless; at 0.18 his 0.154 rate no longer trips the
dynamic rule, but all six of his rug signals are already refused by the ratio gate at 0.82/60.
If the priority is belt-and-braces over money, take 0.15@15 instead (class 2): it keeps the dynamic
frankdegods block alive without the static deny list, costs only RF, and matches the money of the ratio-only
setting exactly. It fails on hoodrat's cohort only because 15 entries is a high bar for a 40-wallet follow list.
5. Sensitivity, one threshold at a time
underAgeMin (ratio 0.80, wallet gate off). The whole rug story is here.
| underAgeMin | rugs refused | rug names | runners lost | paperG2 | live |
|---|---|---|---|---|---|
| 10 | 3 | PENZ, PEZZEL, SEPE | 0 | 0.00 | -50.00 |
| 15 | 5 | + PEZZED, ZEP | 0 | -61.07 | -150.00 |
| 20 | 6 | + PEZ | 0 | -69.71 | -200.00 |
| 30 | 6 | same | 0 | -69.71 | -200.00 |
| 45 | 6 | same | 0 | -69.71 | -200.00 |
| 60 | 6 | same | 0 | -69.71 | -200.00 |
maxLiqToMcap (under 60, wallet gate off). Flat from 0.80 to 0.85, falls off a cliff at both ends.
| maxLiqToMcap | rugs refused | runners lost | names | paperG2 | live |
|---|---|---|---|---|---|
| 0.75 | 6 | 2 | BUSINESS, LIQLORACLE | +214.57 | -71.14 |
| 0.78 | 6 | 1 | BUSINESS | -61.10 | -200.00 |
| 0.80 | 6 | 0 | - | -69.71 | -200.00 |
| 0.82 | 6 | 0 | - | -69.71 | -200.00 |
| 0.85 | 6 | 0 | - | -69.71 | -200.00 |
| 0.90 | 3 | 0 | - | 0.00 | -50.00 |
At 0.90 the gate loses ZEP (0.895), PEZ (0.873) and PEZZED (0.862) and only holds SEPE, PEZZEL, PENZ.
maxWalletRugRate and minWalletEntries (under 20, ratio 0.80; the wallet-gate column is that gate's own
attribution, on top of the ratio gate's constant 6 rugs / 0 runners).
| wrate | wmin | extra rows refused | extra rugs | runners lost | paperG2 of those rows | live of those rows |
|---|---|---|---|---|---|---|
| off | - | 0 | 0 | 0 | 0.00 | 0.00 |
| 0.10 | 5 | 16 | 0 | 8 | -50.18 | +16.83 |
| 0.10 | 15 | 4 | 0 | 1 (RF) | 0.00 | 0.00 |
| 0.12 | 8 | 14 | 0 | 8 | -50.18 | +16.83 |
| 0.15 | 8 (shipped) | 14 | 0 | 8 | -50.18 | +16.83 |
| 0.15 | 10 | 11 | 0 | 7 | -50.18 | +16.83 |
| 0.15 | 15 | 4 | 0 | 1 (RF) | 0.00 | 0.00 |
| 0.18 | 10 (rec) | 3 | 0 | 2 | -31.30 | -26.40 |
| 0.18 | 15 | 0 | 0 | 0 | 0.00 | 0.00 |
| 0.20 | 10 | 3 | 0 | 2 | -31.30 | -26.40 |
| 0.25 | 10 | 3 | 0 | 2 | -31.30 | -26.40 |
| 0.30 | 8+ | 0 | 0 | 0 | 0.00 | 0.00 |
The gate never adds a rug refusal once underAgeMin >= 20, because the ratio gate fires first on every rug row.
minWalletEntries moves the answer far more than the rate does: 0.15 costs 8 runners at 8 entries, 7 at 10 and
1 at 15, with the rate held fixed.
maxAgeMin, one-dimensional, current Gold v2 filters otherwise (under 10, ratio 0.80, wallet gate off),
against the no-age-ceiling pool of 671 rows / 153 tokens. Columns are that gate's own attribution.
| maxAgeMin | rows refused | rugs | runners lost | flat | unk | paperG2 | paperAny | live |
|---|---|---|---|---|---|---|---|---|
| 15 | 628 | CHROME, PEZ | 20 | 37 | 70 | -103.58 | +1409.49 | -116.91 |
| 30 | 614 | CHROME | 18 | 33 | 67 | -37.38 | +1574.40 | -15.03 |
| 45 | 609 | CHROME | 17 | 32 | 67 | -12.22 | +1601.87 | +9.25 |
| 60 (live) | 608 | CHROME | 17 | 31 | 67 | -12.22 | +1625.84 | +9.25 |
| 120 | 602 | CHROME | 17 | 29 | 67 | -12.22 | +1774.20 | +20.83 |
| 180 | 594 | CHROME | 15 | 27 | 67 | -20.83 | +1935.64 | +20.83 |
The age ceiling is flat on Gold v2 between 45 and 120. Tightening 60 to 15 would have refused 20 extra rows
worth -91.36 paper and -126.16 live, so on this tape it would have saved money, but it does so by refusing three
more peak-60 names (AJC, CLANKSEN, PETOI) whose realised pnl was negative anyway. That is the positions lane's
problem, not the entry gate's: do not move maxAgeMin on this evidence. The paperAny column is large only
because it mixes non-Gold books over 123 tokens and should not be read as Gold v2 money.
6. The wallet gate at the recommended threshold
Every wallet blocked at maxWalletRugRate 0.18, minWalletEntriesForRugRate 10, with what it gave the pool.
Rates are from data/watchtower/smartmoney.json as of 2026-09-06T07:33Z.
| Handle | entries | rugs | rate | pool rows | tokens it gave us |
|---|---|---|---|---|---|
| seralberttrades | 11 | 3 | 0.273 | 3 | MUMU (flat, peak 0, paperAny -23.97); PETOI (runner, peak 95.7%, paperAny -35.71); AJC (runner, peak 64.7%, paperG2 -31.30, live -26.40) |
| Binkieee | 17 | 4 | 0.235 | 0 | nothing in the pool |
| Rowdy | 26 | 6 | 0.231 | 0 | nothing in the pool |
| facap | 10 | 2 | 0.200 | 0 | nothing in the pool |
| FartmanSacks | 10 | 2 | 0.200 | 0 | nothing in the pool |
@seralberttrades is the only blocked wallet that touched the pool, and all three of its tokens lost money: the "2 runners lost" line in the Pareto table is +26.40 live and +31.30 paper in hand.
Wallets that clear 0.18@10 but would be blocked by the shipped 0.15@8, with what blocking them costs:
| Handle | entries | rugs | rate | pool rows | what blocking it costs |
|---|---|---|---|---|---|
| fr3ak | 12 | 2 | 0.167 | 7 | hoodrat (peak 238.6%, live +43.23, paperG2 +32.74); MUPPETS (peak 412.6%, paperAny +59.91, paperG2 -24.41); Culio (peak 129%, paperAny -49.20); MADETEST (peak 85.1%, paperG2 -27.21); PasDePanique (peak 63.1%, paperAny -28.22); DOOKIE (flat, paperG2 -29.16); TRUMP2028 (unknown) |
| frankdegods | 39 | 6 | 0.154 | 10 | RF (peak 740.3%, paperAny +96.95); PENZ (rug); plus 5 rug rows the ratio gate already refuses. Statically denied in follower.json regardless |
| ericzhong | 8 | 2 | 0.250 | 2 | PasDePanique (peak 63.1%, paperAny -28.22); TRUMP2028 (unknown). Blocked at 8 entries, cleared at 10 |
| Quanterty | 6 | 1 | 0.167 | 2 | BODKIN (peak 670.2%, paperG2 +97.89); SEPE (rug). Under 8 entries, so never blocked in the shipped config either |
| AvgJoesCrypto | 7 | 1 | 0.143 | 3 | LIQLORACLE (peak 2296.4%, paperG2 +275.67, live +128.86); SEPE and PEZ (rugs). Clears every rate in the grid |
@AvgJoesCrypto is the case that should make anyone nervous about tightening this gate further: the wallet that handed us the tape's best name at +128.86 live also handed us two of the six rugs, at a 0.143 rate that no setting in the grid catches.
7. Caveats
| # | Caveat |
|---|---|
| 1 | n is tiny. 6 rugs and 14 runners in a 41-token pool, over 33 hours. Every threshold here is fitted to single observations: the ratio floor is one token (PEZZED, 0.862), the ratio ceiling is one token (BUSINESS, 0.793), the age binding is one token (PEZ, 16 min). Treat the recommendation as "do not sit on an edge", not as a calibrated number. |
| 2 | One-night family. 5 of the 6 rugs (ZEP, PEZZEL, PEZZED, PEZ, PENZ) are the same launcher on the same night, all with liqToMcap between 0.862 and 0.941 at ages 9-16 min. They are one event, not five. SEPE is the only independent rug in the pool, and its 1.599 ratio is refused by every setting including the old baseline. Effectively the whole rug-side signal is n = 1 launcher + 1 token. |
| 3 | Leakage. Wallet rug counts come from the rolling data/watchtower/smartmoney.json, which is a snapshot at 07:33Z, after the night. For the three night rugs the picture is better than feared: frankdegods' row carries lastTs 2026-09-06T00:00:22Z with entries 39 / rugs 6, i.e. the 0.154 rate was already on disk 25 minutes before the ZEP signal, and 0.154 clears 0.15, so the shipped gate genuinely would have fired live on ZEP, PEZZED and PEZ. @fr3ak (lastTs 09-05T21:24Z) and @seralberttrades (lastTs 09-05T23:09Z) are likewise pre-night. That is a fortunate coincidence of three wallets, not a general property: exact as-of-signal counts for older rows are not reconstructible from disk, so any wallet whose lastTs falls inside the window it is being graded on is leaking. |
| 4 | frankdegods sits 0.004 above the shipped line. 6/39 = 0.1538. One fewer counted rug puts him at 0.128, under 0.15, and the dynamic gate goes silent. A rate gate resting on a 0.004 margin is a deny list with extra steps. |
| 5 | The runner label is a peak proxy. peakPct >= 60 says the price went there, not that the book captured it. 8 of the 14 pool runners closed negative on Gold v2 paper. Every "runners lost" count in this document should be read next to its money columns. |
| 6 | 13 of 41 pool tokens have no outcome data at all (17 of 60 rows). They are counted in the "rows refused" columns and excluded from every rate. Any of them could be a rug or a runner. |
| 7 | The replay releases cooldowns the live system held. Refusing a signal frees the per-token cooldown, so a refused token can re-signal and be taken later in the replay when it could not have been live. This is exactly how the PEZ hole in section 2 opens, and it means the refusal counts here are, if anything, optimistic about coverage. |
| 8 | maxChasePct and maxSignalAgeSec are not applied. Both need state that is not on disk. Some pool rows would have been refused live for chase or staleness, which shrinks the true pool below 60 rows. |