Read the market at a glance
Every active token is a building. Height is market cap, lit windows are holders, the roof tells you if it's pumping, dumping or brand new.
$ open bagcity.fun
BAG CITY renders the live Solana market as one walkable map, then lets you act on it without leaving the street. This page is the source of truth for how it works.
Every active token is a building. Height is market cap, lit windows are holders, the roof tells you if it's pumping, dumping or brand new.
Swap, launch, open the pools, jump into a terminal or burn dust. 16 platforms sit behind 12 landmarks in Bag Square.
Connect read-only and every token you hold lights up gold. The Incinerator shows the SOL stuck in your empty accounts.
One shared chat, live billboards for the hour's biggest moves, and KOL calls checked against the tower.
30% of creator rewards buy back $BAG and burn it every 10 minutes. The burned total is read from the chain, so anyone can check it.
A tower is a pure function of the token's live numbers. Nothing is placed by hand.
// height in city pixels, log scale so $40K and $4B fit on one screen
const height = clamp(10 * (Math.log10(Math.max(mcap, 1e4 )) - 4 ) + 12 , 10 , 84 ) + 8 ;
// lit windows: a lit window is a holder
const lit = Math.min(0.95 , Math.log10(holders + 10 ) / 5.2 );
// what the roof shows
if (ageHours < 24 ) state = 'new'; // crane
else if (chg1h >= 8 || chg24h >= 30 ) state = 'pump'; // beacon
else if (chg24h <= -20 || chg1h <= -10 ) state = 'dump'; // fire
else state = 'hold';
Refreshed every 30 seconds. Heights ease to the new value, so a pump grows on screen.
Each neighborhood is a query over the same live token set, deduplicated so a token lives in one place.
vol1h > 15_000 && chg1h > 0chg1hlaunchpad || age < 7d, chg24h > -60vol1hverified || mcap > 200Mmcap/xstock|stonk/ in name, launchpad, tagsvol24hchg24h <= -50vol24hbillboards: top pump, new holders, volume, newestlivethe twelve landmarksfixedTwelve landmarks around the bag. Each one does one job, from inside the city.
On a phone, every app opens inside Phantom, Solflare or Backpack so it can connect to your wallet.
Connect is read-only. We never ask for a signature; we read the public token accounts for your address.
const programs = ['Tokenkeg…', 'Tokenz…']; // SPL Token and Token-2022
for (const programId of programs)
rpc('getTokenAccountsByOwner', [wallet, { programId }, { encoding: 'jsonParsed' }]);
// amount > 0 → that token's tower lights up gold
// amount == 0 → an empty account: rent you can reclaim at the Incinerator
browser ──▶ Cloudflare Worker (edge cache) ──┬─▶ Jupiter market cap, holders, activity │ ├─▶ DEX Screener pairs, liquidity, links │ ├─▶ CoinGecko candles, trades, terminal │ ├─▶ Magic Eden collections, floors │ └─▶ Solana node TPS, slot, wallets, $BAG supply └──▶ WebSocket ──▶ Durable Object city chat, one shared room
30s20s12–30s5m6–30sThe city runs on these endpoints. Read-only JSON, cached at the edge. Hit Run to see a live response.
const ws = new WebSocket('wss://bagcity.fun/api/chat');
ws.send(JSON.stringify({ type: 'say', name: 'Neon Cabbie 33', text: 'gm' }));
// ← { type: 'hello', history: [...40], online: n }
// ← { type: 'msg', name, text, at } every message pops a bubble on the street
G9Q51Jf7SYer6xv83UCa1myUCTcivMqAEVvUhA8ppump1B supplySOL30%every 10 min// every 10 minutes, on a Cloudflare cron, from a wallet that only ever holds the buyback share
await pumpSdk.distributeCreatorFees(BAG); // permissionless: pays each shareholder, 30% lands here
const solIn = await balance(SOL) - reserve; // keeps a little SOL for fees
const bagOut = await swap(SOL, BAG, solIn); // best Jupiter route, capped slippage
await burn(BAG, bagOut); // SPL burn: supply goes down, on-chain
// verify it yourself: burned = launch supply − supply now
const { value } = await rpc('getTokenSupply', [BAG]);
const burned = 1_000_000_000 - Number(value.uiAmountString);