0x0B Crash dump
How it works
- Re
- Your buy and your sell, simulated on a fork of live Solana before you sign.
- Rev
- api v1
- Status
- Open. Extension, bot, terminal, API.
- Handling
- Read before you buy.
§00 Abstract
Most rugs are one of two things: a sell that cannot land, or a sell that lands for nothing. bsoad finds out which before you buy. When a token reaches your screen, it forks the live Solana state at the tip slot into a local bank, replays what is already in flight against that token, then runs your exact buy and your exact sell inside the fork. If the sell reverts, if a Token-2022 hook rejects it, if a fee eats it, if an authority can freeze or print, if the pool is being pulled, or if the holders turn out to be one wallet with many keys, bsoad crashes your screen and shows a stop code. All of it runs inside the slot you are looking at.
on token(mint):
fork bank@tip <- accounts(mint, pools, hooks, authorities)
replay inflight(mint) // shreds, not yet confirmed
simulate buy(you, mint, route)
simulate sell(you, mint, all) // every live route
read extensions(mint), authorities(mint), lp(mint)
cluster funding_graph(dev, first_buyers, top_holders)
verdict rules + model -> OK | WARN | FATAL
FATAL -> crash(screen, stop_code, evidence, slot)
§01 Trigger
A scan starts the moment a contract address reaches the client: pasted into a trading terminal, opened as a token page, copied from a feed, or born as a new pool. No launchpad is special. Bonding curves, PumpSwap, Raydium AMM v4 and CPMM, Meteora DLMM and dynamic bonding curves, LaunchLab, and a plain SPL mint with a hand-built pool all resolve the same way: from chain state, by owner program, never from a list someone keeps by hand.
input resolve from
CA mint, decimals, token program getAccountInfo(mint)
pools every pool holding the mint owner scans + geyser index
route the path your terminal uses quote(in, out, amount)
programs curve, amm, hook, metadata owner + executable data
token programs
spl-token TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
token-2022 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
metadata metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
§02 State fork
The fork is not a node. It is the smallest set of accounts your trade can touch, copied at the tip slot from a Yellowstone gRPC stream at processed commitment and mounted into an in-memory bank on the Agave SVM. The set is found, not configured: the route is compiled into a real transaction, every key it names is loaded, every program it invokes is loaded with its executable, and every account a Token-2022 transfer hook asks for through its extra-account-metas PDA is resolved and loaded as well. Clock, Rent, EpochSchedule and SlotHashes are copied from the tip, so time-gated logic behaves the way it will on mainnet. The feature set mirrors the cluster. The fork lives for one slot, then it is dropped and rebuilt.
struct Fork {
slot u64 // tip, processed
bank_hash Hash
accounts Map<Pubkey, AccountSharedData>
programs Map<Pubkey, ProgramCacheEntry>
sysvars { clock, rent, epoch_schedule, slot_hashes }
feature_set FeatureSet // mirrored from the cluster
inflight Vec<VersionedTransaction>
ttl 1 slot
}
> fork.load(route.tx)
keys static + address lookup tables
programs owner(ELF) -> ProgramCacheEntry
hook extra_account_metas(mint) -> resolve seeds
sysvars copy@tip
§03 In-flight replay
A rug is usually already moving when you click. Shreds for the current slot arrive before the block confirms. bsoad rebuilds entries from a shred feed, keeps every transaction that writes to an account inside the fork (a remove-liquidity, a set-authority, a freeze, a dev sell, a metadata update) and applies them in leader order before your buy. If the pool is being drained in this slot, your simulated sell meets an empty pool, the same way your real one would a moment later.
shreds udp > fec.recover > entry.assemble > tx.decode
filter tx.writable_keys ∩ fork.accounts ≠ ∅
order (slot, entry_index, tx_index)
apply fork.execute(tx, sigverify = off) // theirs, before yours
switch on fork change: drop, rebuild, rescan
seen this slot, applied before you:
remove_liquidity pool < dev
set_authority mint freeze -> live
transfer dev -> cex deposit
§04 Simulated buy
Your buy is compiled the way your terminal would send it: same route, same slippage bound, same priority fee and compute budget, signed by nothing. Signature checks are off inside the fork and the fee payer is your own public key, so your balances, your rent and your token accounts are real. The buy runs after the in-flight set. bsoad keeps the tokens received, every inner instruction, every log line, every account delta, and the exact state your sell will start from.
tx = compile(route, payer = you, cu_limit, cu_price, slippage_bound)
res = fork.simulate(tx, {
sig_verify: false,
replace_blockhash: true,
inner_instructions: true,
accounts: [you.ata(mint), pool.vaults, curve]
})
res.err == null else STOP BUY_PATH_REVERTS
post_buy = fork.state_after(res)
§05 Simulated sell
This is the part an explorer cannot show you. From the post-buy state, bsoad sells the whole position back through every live route: the pool you bought from, every other pool that holds the mint, and an aggregator route. A honeypot is a sell that reverts. A soft honeypot is a sell that lands and returns dust, because a fee, a hook or a curve parameter takes the rest. A delayed honeypot only fails after an authority acts, so every sell runs twice: once on the state as it is, and once after the worst move any live authority could make next slot: freeze your account, raise the fee to its cap, pause the mint, or move your balance with a delegate.
for route in routes(mint): // pool, other pools, aggregator
now = fork.simulate(sell(you, all, route), from = post_buy)
worst = fork.simulate(sell(you, all, route),
from = post_buy + worst_authority_move(mint))
keep route, now.err, worst.err, now.out, worst.out
FATAL every now.err SELL_PATH_REVERTS
FATAL every worst.err SELL_BLOCKED_BY_AUTHORITY
FATAL hook rejected in fork TRANSFER_HOOK_REJECTS_SELL
WARN now.out under your floor SELL_RETURNS_DUST
§06 Token-2022 extensions
A Token-2022 mint carries its rules as TLV extensions after the base account. bsoad parses every one, and the dangerous ones are run, not only read. A transfer hook executes inside the fork on your sell, with its extra accounts, so a hook that rejects sellers, allowlists the dev, or flips after a set slot is caught by running it, not by trusting its source. Transfer fees are computed from the epoch-scheduled config, so a raise already queued for the next epoch counts against you now.
extension read fatal when
TransferHook program_id, extra_metas rejects your sell in the fork
TransferFeeConfig older, newer, max_fee fee over your limit, now or queued
PermanentDelegate delegate set to a live key
DefaultAccountState state Frozen
NonTransferable - present
Pausable authority, paused a live key can pause
MintCloseAuthority authority live
ConfidentialTransfer auditor, auto_approve balances hidden from the check
MetadataPointer update_authority live (warn)
ScaledUiAmount multiplier, authority live (warn)
§07 Authorities
Classic SPL rugs need no code at all. A live mint authority can print supply into the pool. A live freeze authority can freeze your token account the slot after you buy, and your sell fails with AccountFrozen. A live metadata update authority can change the name and the image. If the token runs through a custom program, that program's upgrade authority can change what it does after you are in. bsoad reads every authority your trade depends on and asks each one the same thing: is the key live, and what is the worst it can do to a sell.
authority where worst move stop code
mint Mint.mint_authority print into pool MINT_AUTHORITY_LIVE
freeze Mint.freeze_authority freeze your ATA FREEZE_AUTHORITY_LIVE
metadata Metadata.update_authority rename, re-image METADATA_MUTABLE
program ProgramData.upgrade_auth swap the code PROGRAM_UPGRADEABLE
pool admin AmmConfig, pool authority pause, reroute fees POOL_ADMIN_LIVE
hook program hook ProgramData reject sellers later HOOK_PROGRAM_UPGRADEABLE
§08 Liquidity and pulls
On a bonding curve the check is the curve's own state and whether migration is complete. On a pool it is one question: who can take the liquidity out. LP tokens burned, LP tokens locked in a program with a real unlock, or LP tokens sitting in a wallet that can withdraw in one instruction. A pull already in flight is caught by the in-flight replay. A pull that is only possible is reported with the wallet that can do it and the cluster it belongs to.
lp = pool.lp_mint
holders(lp):
burn address ok
locker(program) read unlock, read locker upgrade authority
wallet WARN LP_WITHDRAWABLE who: cluster(wallet)
inflight remove_liq FATAL LP_PULL_IN_FLIGHT
§09 Funding-graph clustering
Wallets lie about being many. Starting from the dev, the first buyers in the creation slot and the slots right after it, and the top holders, bsoad walks SOL and token transfers backwards until every walk reaches a known source: an exchange hot wallet, a bridge, a fan-in shaped like a mixer, or the depth limit. Wallets join one cluster when they share a funder, share a rent payer, co-sign, land behind one Jito tip in a single bundle, or carry the same compute-budget fingerprint. The output is the clusters, the supply each one holds, and the edges that prove it. A bundled dev cluster sitting on the float is a rug that has not happened yet.
seeds dev ∪ first_buyers(slots 0..k) ∪ top_holders(n)
walk bfs over transfers, reverse time, depth ≤ D
stop cex_hot | bridge | mixer_fanin | depth
merge union_find on shared_funder | shared_rent_payer
| co_signer | same_bundle_tip | same_cu_profile
emit Cluster { members, funders, supply_share, edges[] }
FATAL dev ∈ C and C.supply_share over your limit BUNDLED_DEV_CLUSTER
WARN funder(dev) funded a token that was rugged DEV_FUNDER_RUGGED_BEFORE
§10 The model
Hard failures do not need an opinion. A reverted sell is fatal whatever a model says. Everything softer goes to a model trained on the on-chain traces of past launches: simulation outcomes, the extension set, the authority map, cluster shapes, in-flight patterns. It returns one of three verdicts, writes the stop code, and writes one plain line about why. It never writes safe. The most it can say is that nothing was found in this slot.
features sim{buy, sell, worst} ⊕ ext ⊕ auth ⊕ lp ⊕ graph ⊕ inflight
rules hard -> FATAL // no model involved
model soft -> p(rug | evidence) // never shown as a number
verdict OK_NOTHING_FOUND | WARN | FATAL
explain stop_code, one line, evidence refs
§11 Crash protocol
When the verdict is fatal, bsoad takes the screen. Your terminal stays where it is, but a blue screen covers the buy button with the stop code, the evidence and the slot it was found in. It does not close by clicking away. To go on anyway you hold a key while the evidence stays in view. Every new slot is a reboot: the fork is rebuilt, the scan runs again, and the crash lifts on its own when the reason is gone: an authority revoked, LP burned, a hook removed.
FATAL -> overlay(tab, z = top)
show stop_code, evidence, slot
block buy
go on hold(key) with evidence in view
slot+1 -> reboot: rebuild fork, rescan
lift crash if verdict != FATAL
§12 Stop codes
stop code caught by meaning
SELL_PATH_REVERTS simulated sell you can buy, you cannot sell
SELL_BLOCKED_BY_AUTHORITY worst-move sell one key away from no exit
TRANSFER_HOOK_REJECTS_SELL hook run in fork the token's own code says no
TRANSFER_FEE_ABOVE_LIMIT fee config the exit is taxed past your limit
SELL_RETURNS_DUST simulated sell it lands and pays you nothing
FREEZE_AUTHORITY_LIVE mint read your account can be frozen
MINT_AUTHORITY_LIVE mint read supply can be printed
PERMANENT_DELEGATE_SET extension read your balance can be moved
DEFAULT_STATE_FROZEN extension read new accounts start frozen
LP_PULL_IN_FLIGHT in-flight replay the pool is leaving this slot
LP_WITHDRAWABLE lp holders one instruction from empty
BUNDLED_DEV_CLUSTER funding graph the dev is most of the float
HOOK_PROGRAM_UPGRADEABLE program data the rules can change later
BUY_PATH_REVERTS simulated buy the route itself is broken
§13 Non-goals
No calls. No picks. No signals. No safe badge. bsoad never holds keys and never signs. It does not trade for you. It refuses to let a known way to lose the whole bag pass unseen.
calls no
picks no
safe badge no
custody no
signing no
§14 Channels
Three ways in, one engine. The extension works inside the page: when a site asks Phantom, Backpack or Solflare to sign, it sends the transaction to the API first and shows the stop code before the wallet opens. On Axiom, GMGN, pump.fun and DexScreener it puts a face next to every coin. The bot answers any CA you paste and watches the wallets you give it. The API gives the same verdict to your own code; the terminal runs on it.
channel where what it does
extension /extension.zip sign call -> POST /v1/simulate -> stop code before the wallet opens
:) :| :( next to every coin on your screener
bot t.me/bsoadbot CA in, verdict out; /watch a wallet, alert when a coin turns
api /v1 /docs token, tokens, simulate, wallet, stream
terminal /app your wallet live, quick check by CA
— end of dump —