bsoad rugcheck
36%

Forks Solana. Simulates your sell before you buy.

fork state ok

sim buy running

sim sell queued

stop code SELL_PATH_REVERTS

move to tear. click to reboot.

bsoadcrash dump

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

§15 Raw dump


    

— end of dump —