Pangu docs

Rubric

Meteora's bounty mapped line by line to Pangu's code, its pages, its tests, and live transactions.

This page is copied as is from the repository's own rubric file, so nothing in it is trimmed or softened for a judge reading it here.

Pangu is a rules program that Meteora's Dynamic Bonding Curve (DBC) attaches to a new stock token for the length of its first sale, and the app around it: a launch page for issuers, a sale page for buyers, a console for verifiers and a portfolio for anyone holding. DBC finds the price. Pangu decides who may receive tokens and how many, then DBC removes Pangu from the token for good when the sale fills, or every rule lifts when the sale's offering period ends, whichever comes first. One line pitch: we help stock token issuers run a fair first sale on Meteora DBC, where the curve finds the price and no bot or whale can take more than their share.

Every line below points at three kinds of proof: the program (code, test, live transaction), the page in the app that puts it in front of a person, and the recorded run that drove that page on devnet or on a forked copy of mainnet. The page runs live in packages/web/lab-evidence, one text file each.

1. Meteora's bounty, line by line

Source: the program page's "Meteora: $5,000, Best Use of Meteora DBC" section, copied verbatim.

Their wordsWhat Pangu doesIn the app, and its proofWhere in the codeTestLive transaction
"you control the curve shape, fee schedule, quote token"Pangu does not change any DBC curve setting. It reads the pool's own launch template and refuses only what would hurt buyers: fees paid out in the sale token instead of the paying token (see the row below), a paying token the issuer can freeze, and a price band on a sale not paid in a dollar the program lists. Every other curve, fee and quote choice stays the issuer's./launch sets the curve's shape (shares, raise target, share kept for the pool) and the paying token, and redraws the curve before anything is signed. launch-devnet.txt: PLAUNCH paid in the demo dollar and PLAUNCHL paid in devnet SOL, every term read back from devnet equal to the form. network-fork.txt: the same page built for mainnet launched a USDC sale and an AAPLx sale on forked mainnet.packages/scripts/src/curve.ts and packages/web/lib/launch-curve.ts build the DBC template; programs/pangu/src/instructions/create_sale.rs reads it backtests/create_sale.ts "reads the same launch template the Meteora SDK does"template tx 526NJoSp, devnet-run.md; a template sent from the launch page, 5gvhKyFW, metadata-devnet.txt. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"graduation threshold, and how the pool migrates into Meteora DAMM v2 liquidity"Pangu leaves graduation entirely to DBC. It only removes itself as a side effect: when the curve fills, DBC's own migration strips the transfer hook from the mint in the same trade.The raise target on /launch is the graduation threshold. A sale page shows a graduated sale and names its DAMM v2 pool, and the issuer's controls on it include moving the curve to DAMM v2 once it fills. /sales lists PLIST as graduated. On forked mainnet a USDC curve under a ceiling was bought to graduation and migrated, a step anyone may call, mainnet-rehearsal.md a7 and a8.docs/measurements/fork-test.md (migration step); no Pangu code runs here by designfork-tests/life.ts "k. fresh buyers fill the curve and DBC takes the hook off the mint" and "l. anyone can migrate the graduated pool to DAMM v2"migration tx 5CpV8KRe of the graduated list sale PLIST, mint 2ARD1KwvxyjLPwe46rivjxPRyMzvxSEPGvwKTqcNXpFR, into DAMM v2 pool 2cEAoE9zi53y736DsaPBzgfdrUqJnVgqwutZTGsmsSLc, docs/measurements/devnet-run.md, "The fourth run". mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"We want to see what it looks like for tokenized stocks"The sale rules are stock-sale specific: a per-wallet cap so no single buyer takes the float, an optional approved-buyer gate, and an optional price ceiling measured against the stock's real market price, not just a curve number.Every sale page states its ceiling, the stock price it follows and what buyers pay in, and asks the rules before a buyer signs. /launch offers a ceiling on Apple's exchange price or on AAPLx, which trades all week. sale-devnet.txt: a buy on PAAPLX, MtunmJmH, after the rules answered pass.programs/pangu/src/instructions/execute.rs (the hook decision)tests/buys.ts, tests/approvals.ts, tests/credential.ts, tests/band.tssixth run: on PBAND2 (mint 5VrNEfV1gQrBrMLSxyaK3AXRa2yj9Xp9i65MZzKHGZSo) the cap refused 2jbjpk64 OverCap and the ceiling refused 4LXby4Fw PriceOutsideBand; on PVRFD (mint 7ixMAMUmysN4qsCpthdznhq7aRbiCNB5Xeg3X9vBSLWn) the verifier gate refused 2dvaB2gc CredentialInvalid. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"Build something on DBC that outlasts the current meme-stock meta"Meteora's own push this quarter (DAMM v2 0.2.4, DBC stock tokens as the paying token, StockLaunch on 15 Sep) is toward stock-paired launches generally, not one memecoin. Pangu is indifferent to which token pays: the same rules program covers a dollar-priced sale and a stock-paired one, and a launchpad like StockLaunch is a second kind of customer for it, not only an issuer.The app built for mainnet offers USDC and the tokenized stocks AAPLx, TSLAx, NVDAx and SPYx as paying tokens on /launch, each only while its Meteora token badge reads back from the chain. network-fork.txt: an AAPLx-paid sale launched from the page on forked mainnet and took a buy paid in AAPLx.packages/web/lib/network.ts (the paying tokens per network); fork test buys with real AAPLxfork-tests/stock-quote.ts "a. the partner creates a template quoted in AAPLx" and "d. an approved buyer buys under the cap, paying in AAPLx"; packages/sdk/fork-test/rehearsal.ts b1 to b5forked mainnet only: mainnet-rehearsal.md b5, 1 AAPLx bought 30,992,545,499,318 raw units and selling half returned 0.50483727 AAPLx. Not run live on devnet or mainnet. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"launch mechanics tuned for equity-like assets (price discovery for thinly traded or newly tokenized stock pairs)"The price band is the equity-specific mechanic: a buy is refused if it would push the curve price more than band_bps above the live stock price, so early illiquidity cannot be used to print a runaway price against latecomers. Sells are never blocked by the band.The /launch preview places the ceiling on the curve against the live stock price and says where buying stops ("At $356.05 today, buying stops once 66 percent of the curve has sold", network-devnet-launch-shots.txt). The sale page checks the ceiling before signing, and asks the server for a fresh Pyth price when the stored one is stale. network-fork.txt: a further buy still under the cap refused by the ceiling before signing.programs/pangu/src/instructions/execute.rs band check; programs/pangu/src/price.rstests/band.ts, threat model C9demo sale mint 5VrNEfV1gQrBrMLSxyaK3AXRa2yj9Xp9i65MZzKHGZSo (PBAND2), open, priced in a demo dollar, offering period to 7 October 2026: a buy past the ceiling refused live on devnet with PriceOutsideBand, 4LXby4Fw, docs/measurements/devnet-run.md, "The sixth run". The seventh run's PAAPLX, banded on AAPLx which publishes all week, refused the same way, 5TbBnGrV, and the fifth run's PBAND, 3FMhcSoC. The same refusal is also proven in the fork test, docs/measurements/sdk-fork-test.md. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"novel curve or fee configurations"Partly. Pangu invents no curve shape; it fixes the configuration a stock sale needs and refuses anything else: fees collected only in the paying token so no fee movement ever passes through the rules, minting power revoked at launch so the supply buyers see is final, a Token-2022 base with Pangu as its hook, graduation into DAMM v2, and a ceiling only on a listed dollar. The novelty Pangu claims is in the graduation rules, not the curve./launch fixes those settings in every template it sends; an issuer chooses only the shape, the paying token and the rules. launch-devnet.txt and metadata-devnet.txt: every launched sale read back from devnet with those terms.packages/sdk/src/dbc/template.ts FORCED and withoutMintAuthority; create_sale.rs handle_create_sale (fee mode and mint authority proven on chain)packages/sdk/test/dbc.test.ts "the settings a Pangu sale forces"; tests/create_sale.ts "refuses a template that collects fees in the sale token" and "refuses a mint that can still be minted"devnet: every sale in docs/measurements/devnet-run.md and every page launch in launch-devnet.txt was opened through this template; mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"creative graduation rules"Pangu's "creative" part sits before graduation, not at it: the per-wallet cap, the two approval modes (issuer list or a Solana Attestation Service credential), the price band, and the offering period that lifts every rule if the curve never fills. They are the graduation-time state Pangu leaves behind for DBC to migrate cleanly, since a fair pre-graduation sale means a fair set of DAMM v2 holders on day one./launch sets the offering period (1 to 60 days, or none), and every sale page and /portfolio row says when the rules lift. launch-devnet.txt: PLAUNCH's end read back from devnet as 2026-10-07T14:12:52Z, 14 days after launch. /verify issues the credentials the second approval mode reads, verify-devnet.txt.ARCHITECTURE.md, "The hook decision"; state.rs SaleRules::offering_is_overfork-tests/life.ts, steps d to o and "p. once the offering period ends, every rule lifts before graduation"; tests/offering.tsthe PLIST migration tx above; PBAND2 and PAAPLX both opened with an offering period ending 7 October 2026. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"tooling that helps issuers configure and monitor DBC pools"The app is that tooling for a person, and the TypeScript package and scripts are that tooling for a developer: one form or one command creates a template, a pool and the rules together, and one page or one command shows every sale and its standing./launch configures a sale; /sales shows every sale on the chain by scanning the program; a sale page gives its issuer the controls to approve buyers, claim fees and migrate; /portfolio lists every sale a wallet issued with its buyers and its raise; /verify runs the credential side. sale-devnet.txt: an issuer approved a wallet from the page, hcv8L5CM. portfolio-devnet.txt: the issued list held exactly the 7 sales the directory names.packages/web/app and packages/web/lib; packages/sdk; packages/scripts/src178 SDK tests, 126 script tests, and the page proof scripts in packages/web/lab-evidencedevnet-run.md, every command in section 4 below, and the signatures at the foot of each docs page for the app. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet
"Working code on mainnet beats slides"Devnet is fully proven: the program deployed, upgraded six times in place, and run end to end with real transactions across eight recorded runs, and each page of the app driven on devnet. Mainnet is ready: a reproducible, verified mainnet build, a guarded deploy only a person runs, and a rehearsal of the deploy, the sales in USDC and AAPLx, and the upgrade key's handover to a Squads multisig, all on a forked copy of mainnet.One setting builds the app as the mainnet product. network-fork.txt: that build ran its launch, sale and portfolio pages against the forked mainnet, simulate-only attack ledger and no demo dollars included.scripts/wsl/verify-build.sh, deploy-mainnet.sh, mainnet-rehearsal.sh; packages/web/lib/network.tsmainnet-rehearsal.md: VERIFY-BUILD-OK, DEPLOY-MAINNET-OK with HASH MATCH: YES, SQUADS-UPGRADE-OK, MAINNET-REHEARSAL-OKdevnet program 4Nd46mDiaTSkqXPAXKqT4jkahcz1TxVSdoirbBCAr5qG, docs/deployments.md. mainnet: ready, not deployed; one command the team runs with its own key, rehearsed on forked mainnet. This is the one bounty line not yet true, and the runbook to make it true is docs/deploy/mainnet.md
Judging: "originality of the DBC configuration or use case"The hook-pool rules layer plus the equity price band is not something the fork test or the mainnet pool listing shows elsewhere.A stock issuer's whole first sale in one app: launch, buy, verify, portfolio.nonenone, a research finding not a testnone
Judging: "technical soundness"31 Rust tests, 141 unit tests (on solana-bankrun, against a v0 build of the same source, because bankrun cannot load SBPF v3), 38 forked mainnet steps (the program's full sale life against real cloned DBC, DAMM v2 and attestation programs, on the SBPF v3 build devnet runs; the SDK's own fork suite adds its 16 steps on the same validator and build, run the same day through a keyed mainnet node), 178 SDK tests, 126 script tests, 10 randomized attack sequences of 6,000 operations, eight live devnet runs with every refusal landed as a real transaction and all three access modes attacked on the live program, a full code review with findings fixed, the program audit of 23 September that added C18 to C21, and the code review of 24 September that added C22 to C25.The four page proofs, launch, buy and sell, verify and portfolio, each a script that drives the real page on devnet with a throwaway wallet and checks every line against the chain; and the forked-mainnet rehearsal, which ran the verified mainnet build's deploy, sales in USDC and AAPLx, the Squads handover, and the mainnet build of the app.see section 4 for exact commandsall of the abovetest-counts.md, devnet-run.md, fork-test.md, random-sequences.md, mainnet-rehearsal.md, and launch-devnet.txt, metadata-devnet.txt, sale-devnet.txt, verify-devnet.txt, portfolio-devnet.txt, network-fork.txt
Judging: "whether the idea has a life after the hackathon"The product an issuer and a verifier need is already built: launch, buy, verify, portfolio. Mainnet is ready, with a runbook. A launchpad like StockLaunch is a second customer type; the upgrade key stays with the team at launch so a bug found early can be fixed, then moves to a two of three Squads multisig and is frozen after an independent audit; and Dynamic Fee Sharing is scoped as a next step to give a verifier a reason to keep approving buyers./launch, /verify, /portfolio, and the mainnet page of the docsnonenone, a roadmap statementnone

2. Threat model section C, invariant by invariant

Source: docs/security/threat-model.md section C. Devnet transactions are from the second run in docs/measurements/devnet-run.md (the upgraded, reviewed build), unless marked otherwise. C18 to C21 came with the rules v2 build, the fifth deploy of 23 September 2026, and their evidence is from the sixth run. C18's dollar list came with the sixth deploy of 23 September 2026, and its refusal landed in the eighth run. C22 to C25, and the per-process bound in C15 and C17 and the lifted rows in C16, came with the code review of 24 September 2026.

#Invariant, plain sentenceFile and functionTestDevnet transaction
C1Calling the hook directly, outside a real token transfer, changes nothing and fails.execute.rs, the "genuinely mid-transfer" check at the top of the functiontests/direct_call.ts "fails and moves no counter when nothing is being transferred"3PUW1bT9, NotTransferring
C2Every account the decision reads is proven to be the real one for its role, not a look-alike.execute.rs account derivation checkstests/create_sale.ts "refuses a pool account that is not owned by DBC"; tests/credential.ts "refuses an attestation owned by another program" and "refuses another wallet's attestation, wherever it is put"; tests/band.ts "refuses a price account owned by another program" and "refuses a pool account swapped for another DBC pool"4HnSQqse, BuyerRecordMissing. Credential mode, now landed on devnet: 2dvaB2gc, CredentialInvalid, a buyer with no attestation from the sale's verifier, sixth run (table below)
C3A wallet's net tokens bought never exceeds the cap, however many token accounts it uses.execute.rs, net_bought + amount <= cap check keyed on wallet owner, not token accounttests/buys.ts "refuses a buy one unit over the cap", "adds buys together, so two small ones cannot pass the cap" and "counts two token accounts of one wallet against one cap"; tests/random.ts, the randomized sequences in docs/measurements/random-sequences.md2PJuHfzp, and a second-account attempt 2zK4gSTC, both OverCap
C4During the sale a token can only move between a wallet and the pool vault, never wallet to wallet.execute.rs, the wallet-to-wallet branchtests/transfers.ts "refuses a move from one wallet to another"4k9Aiot3, WalletToWalletDuringSale
C5A holder can always sell back to the pool. No missing account or stale rule can block a sell.execute.rs, the sell branch reads only the source owner's record if it existstests/sells.ts "lets a revoked wallet sell everything it holds" and "lets a wallet with no record at all sell"; tests/random.ts, the 1,330 simulated exit sells in docs/measurements/random-sequences.mdWiANjhTm, a revoked wallet's sell goes through
C6With the approved-buyer list on, tokens leave the pool only to a wallet approved at that moment.execute.rs mode 1 branch, approve_buyer.rs, revoke_buyer.rstests/buys.ts "refuses an unapproved wallet when the issuer's list is in force" and "stops a revoked wallet from buying again"549vJCdN, NotApproved
C7The sale rules and the extra-accounts list for a mint are created once, only by the pool's creator, in the same transaction as the pool.create_sale.rs, creator == signer check, init constrainttests/create_sale.ts "refuses a signer who did not create the pool" and "cannot be run twice for the same mint"pool and rules transaction 23s4BVPA
C8Rules that affect buyers cannot change invisibly; the cap can never change after the first trade.create_sale.rs (rules set once, no update instruction), SaleCreated eventno test: the proof is an absence, lib.rs declares no instruction that changes a sale's rules; tests/create_sale.ts "cannot be run twice for the same mint"none needed, absence of an instruction is proven by the IDL, not a transaction
C9A banded buy only succeeds with a fresh, positive, correctly-sourced price inside the band; a doubtful price refuses the buy and never the sell.execute.rs band branch, price.rstests/band.ts4LXby4Fw, PriceOutsideBand, a buy past the ceiling on the sixth run's demo sale PBAND2, docs/measurements/devnet-run.md, "The sixth run"; 5TbBnGrV, the same on the seventh run's PAAPLX, banded on AAPLx; and 3FMhcSoC, the same refusal on the fifth run's PBAND; also proven in the fork test, docs/measurements/sdk-fork-test.md. Earlier, 7pNLsop6, PriceStale (no live gateway that morning)
C10All counter math is checked; a sell larger than a wallet's recorded buys floors the record at zero, never underflows.execute.rs, saturating_sub on the sell pathtests/sells.ts "floors the record at zero when the sell is larger than it"; tests/random.ts, the randomized sequences in docs/measurements/random-sequences.mdnone isolated on devnet, covered by the 1,330 simulated exit sells in those sequences
C11The launch template must pay fees in the paying token only, never the sale token.create_sale.rs, collect_fee_mode == QuoteToken check (byte 232 of the template)tests/create_sale.ts "refuses a template that collects fees in the sale token", Rust test against the real mainnet template bytesnone live (every devnet launch already uses QuoteToken by construction)
C12Scripts never hold or print a private key; every address that receives money is shown to the signer before signing and read back after.packages/scripts/src/wallets.ts, chain.tspackages/scripts/test/environment.test.ts "never quotes a base58 secret back", packages/scripts/test/wallets.test.ts "brings every wallet's SOL back when the run throws partway", and the rest of the scripts suite (126 tests)devnet-run.md, no key ever printed in any of the five commands' output
C13Tokens leave the pool vault only into a token account whose owner can never change (an associated token account).execute.rs, ImmutableOwner extension check on the destinationtests/buys.ts "refuses a buy into an account whose owner can still change"5mKWFBtb, ReceivingAccountOwnerCanChange
C14No sale token can come into existence except from the pool vault; create_sale refuses a mint whose minting power is still live.create_sale.rs, mint authority checktests/create_sale.ts "refuses a mint that can still be minted"first launch attempt against the upgraded program was refused with MintAuthorityStillSet (error 6021), see devnet-run.md "The new rule bit first"
C15The demo dollars route is the one server path that holds a key: it mints only the demo dollar, at most one grant per wallet per hour and ten a minute, with both slots reserved before any await, and the key never reaches a client, a response or a log. The limits count per server process, so on several instances they multiply; on mainnet the price refresher's spend is bounded by its key's 0.05 SOL floor, not by the counter alone.packages/web/lib/demo-dollars.ts, reserve, keep, release and mintAuthority, imported only by app/api/break/dollars/route.ts; the spend floor in refresh in packages/web/lib/price-refresh.tspackages/web/lab-evidence/break-dollars.txt, 23 Sep 2026: a second call from the same wallet refused with 429, and a race of 12 fresh wallets at the same moment where 10 got past the reservation and 2 were refused with 429the grant in that file, Vs83nVSS
C16A transaction the app builds for a visitor moves value only between that visitor and the pool vault, or to an account the visitor owns, never to a party the page chose, and every row states its cost before the wallet is asked to sign. Once the offering ends and a row's refusal has lifted, the row does not send or restates its cost: the wallet-to-wallet row is simulate-only then.packages/web/lib/break.ts, the row builders, liftedByOffering and simulateOnlyWhy, with the send switch in packages/web/components/break/break-section.tsxthe lifted rows: the app's review-fixes run. packages/web/lab-evidence/break-simulations.txt, 23 Sep 2026: all nine rows run on PBAND, every row's promise equal to the chain's answer, 0 off the standardthe honest buy sent in that run, 3LGW2KfH
C17No public route lets an outsider spend the RPC budget the live numbers depend on: the price route answers only for mints in the app's own sale list, from a ten second cache. The caches and limits are per server process, so several instances multiply the reads; the SOL spent on price posts stays bounded by the refresher key's floor (C15).packages/web/app/api/price/[mint]/route.ts, the openedSales check and the ten second cache; packages/web/lib/pulse.ts, the shared reading; the spend floor in refresh in packages/web/lib/price-refresh.tsby inspectionnone, an off-chain limit
C18A banded sale is paid in a dollar the program lists for its network: USDC on mainnet, devnet USDC and the demo dollar on devnet. The paying token is the one the launch template names, checked and stored for every sale.create_sale.rs, check_band with DOLLAR_MINTS, and handle_create_saletests/band.ts "refuses a band on a sale priced in wrapped SOL", "refuses a band on a six decimal token that is not on the dollar list", "refuses a band on a Token-2022 stock token" and "refuses a paying token the template does not name"; packages/sdk/fork-test/life.ts step p; mainnet-rehearsal.md b3 and c, the mainnet build refusing a ceiling on AAPLx and on the demo dollarPBAND2 opened on the rules v2 build banded and priced in the demo dollar 2TYsrKmXKrqxLRULNBGFrGjTnxebo1H2azRb7bzQPem5, which its rules now store: sale 5Biy9QvC, sixth run. On the sixth deploy of 23 September 2026, a ceiling on a fresh six-decimal token that is not on the list refused with BandNeedsDollarQuote: M9GmvWbF, eighth run
C19The issuer cannot freeze the paying token, so the exit in C5 is never the issuer's to close.create_sale.rs, handle_create_saletests/create_sale.ts "refuses a paying token the issuer can freeze" and "opens on the same kind of paying token with no freeze authority"both sixth run sales passed this check to open: PBAND2 5Biy9QvC and PVRFD 3NGu6Wxg. No devnet transaction has been refused with IssuerControlsPayingToken
C20The cap is below what the curve sells, so no single wallet can buy the whole sale.create_sale.rs, handle_create_sale and read_swap_base_amounttests/create_sale.ts "refuses a cap equal to everything the curve sells" and "opens with a cap one unit below what the curve sells"PBAND2's cap is 1,099,999,999 raw units against the 10,999,999,990 its curve sells, sale 5Biy9QvC, and the cap held on devnet: 2jbjpk64, OverCap, sixth run. No devnet transaction has been refused with CapCoversWholeSale
C21The offering period is fixed when the sale opens and cannot change. While it runs every rule holds; when it ends every rule lifts, so a curve that never fills cannot lock the token for good.create_sale.rs handle_create_sale (EndInThePast), execute.rs handle_execute, state.rs SaleRules::offering_is_over, close_buyer_record.rs handle_close_buyer_recordtests/offering.ts, all five cases; the Rust test the_offering_is_over_only_from_a_real_end_time_on_a_readable_layout in state.rs; fork-tests/life.ts step p, where the rules lifted at the end on forked mainnetPBAND2 opened with its offering ending 7 October 2026 at 10:38:05 UTC, sale 5Biy9QvC, and every rule held on it afterwards: 2jbjpk64 OverCap and 4LXby4Fw PriceOutsideBand, sixth run. PAAPLX, with an end of 7 October 2026 at 13:05:32 UTC, held the same way in the seventh run. The lift itself has not happened on devnet: it falls after judging
C22A buy or a sell the app builds is refused on chain unless it returns at least what the page showed, less the 1 percent slippage the page states. The floor comes from the amount shown, not from a fresh quote taken while building.packages/sdk/src/dbc/trade.ts, minimumAmountOut and minimumQuoteOut; packages/web/lib/trade.ts, floorOf, buildBuy and buildSell, from packages/web/components/sale/trade-panel.tsxthe app's review-fixes runthe app's review-fixes run
C23Every place a buy is offered shows the exact moment the offering ends and the time left, and flags an offering under 24 hours long: at that time every rule lifts and anyone can buy without a cap. A display rule, decided on 24 September 2026, not a program floor: the harm is a buyer not knowing, which the page fixes, and a floor would forbid an honest short sale.packages/web/components/sales/words.ts, offeringEnd, shown by components/sale/trade-panel.tsx, sale-desk.tsx and the sales and portfolio rowsthe app's review-fixes runnone, a display rule
C24The mainnet deploy tool proves by itself that the binary is the mainnet build (SBPF v3, carrying mainnet USDC and neither devnet dollar) before it reads the network; the typed hash is a second check, never the only one.scripts/wsl/lib-binary-checks.sh, sbpf_version and check_mainnet_list, run by deploy-mainnet.sh and verify-build.shthe devnet build, the SBPF v0 unit-test build and a three byte file, each given its own correct hash, all refused before any network call; docs/measurements/mainnet-rehearsal.mdnone, it runs before any transaction
C25Storing a launch's logo and details on Irys never takes more than 0.02 SOL from the issuer's wallet, and the price is shown before the wallet is asked.packages/web/lib/token-metadata.ts, MAX_STORAGE_LAMPORTS, storagePrice and uploadMetadatathe app's review-fixes runthe app's review-fixes run

Credential mode, now landed on devnet

Until the sixth run, credential mode was proven in the unit tests and the fork test only. The sixth run opened a credential sale on the live program, PVRFD, mint 7ixMAMUmysN4qsCpthdznhq7aRbiCNB5Xeg3X9vBSLWn, sale 3NGu6Wxg, and attacked it. 10 attacks run, 8 refused as expected, 2 allowed as expected, 1 not applicable (the ceiling, since this sale has no band), 0 off the standard. The verifier's side, setting up, issuing, checking and revoking a credential from the /verify page, is in packages/web/lab-evidence/verify-devnet.txt.

AttackInvariantResultDevnet transaction
buy with no buyer recordC2BuyerRecordMissing4jtZjc3K
buy with no attestation from the sale's verifierC2CredentialInvalid2dvaB2gc
an attested wallet buys under the capC3goes through3ym4DSTu
buy past the cap in one goC3OverCap3dg8z3XT
a second buy that crosses the capC3OverCap4EkrP5iH
buy into a second token account of the same walletC3OverCapwjCE8fHG
buy into an account whose owner can still changeC13ReceivingAccountOwnerCanChange492nnf4q
send tokens straight to another walletC4WalletToWalletDuringSale48ruUbgr
call the hook on its own, with no transferC1NotTransferring5KZe7mM3
an attested wallet sells back to the poolC5goes through2p97iWgJ

3. Why not Meteora's own Alpha Vault or Presale Vault

  • Alpha Vault only sits in front of DLMM and DAMM pools. It cannot attach to a bonding curve at all, so it cannot run during price discovery, which is where a stock token's first buyers need protecting.
  • Alpha Vault sells at one fixed price. Pangu's rules run inside a live curve, so the price is still discovered, not fixed.
  • Presale Vault (beta, program presSVxnf9UU8jMxhgSMqaRwNiT36qeBdNeTRKjTdbj) is a standalone sale before any market exists: fixed price, first come, or pro rata. No curve, no price discovery.
  • Pangu's rules run inside the DBC curve all the way to graduation, and selling back to the pool is always allowed, which neither Meteora product guarantees because neither is built for an ongoing curve.

4. Run the proof yourself

In the browser: open the app, connect a devnet wallet, and run the nine rows of "Try to break it" on the front page, then buy and sell on any sale page, launch a sale from /launch, and look it up on /portfolio. The recorded runs of each page are in packages/web/lab-evidence: launch-devnet.txt, metadata-devnet.txt, sale-devnet.txt, verify-devnet.txt, portfolio-devnet.txt, and network-fork.txt for the mainnet build on forked mainnet.

All Rust and TypeScript tests, no network needed:

MSYS_NO_PATHCONV=1 wsl -d Ubuntu -- bash /mnt/d/Projects/Meteora/scripts/wsl/test.sh

Prints rust: test result: ok. 31 passed and a mocha summary of 141 passing, then exits 0.

Full sale life against real cloned Meteora and attestation programs, on a forked validator:

MSYS_NO_PATHCONV=1 wsl -d Ubuntu -- bash /mnt/d/Projects/Meteora/scripts/wsl/fork-test.sh

Prints 38 passing fork tests, including the AAPLx-paid launch and the DAMM v2 migration, and exits 0. Takes about 5 minutes; run detached and tail the log for anything longer, per the script's own header comment.

The mainnet build and its rehearsal, on a forked copy of mainnet, nothing sent to mainnet:

MSYS_NO_PATHCONV=1 wsl -d Ubuntu -- bash /mnt/d/Projects/Meteora/scripts/wsl/verify-build.sh
MSYS_NO_PATHCONV=1 wsl -d Ubuntu -- bash /mnt/d/Projects/Meteora/scripts/wsl/mainnet-rehearsal.sh <the sha256 verify-build.sh printed>

The first builds the mainnet binary twice and ends VERIFY-BUILD-OK once both hash the same, the hash docs/deployments.md records. The second deploys that binary to the fork, runs the USDC and AAPLx sales and the Squads handover, and ends MAINNET-REHEARSAL-OK, in about fifteen minutes. The last run of both is written up in docs/measurements/mainnet-rehearsal.md.

The devnet prove command, against the live program:

cd packages/scripts && npm run prove

Prints a table of attacks, an exit code, and the largest holder's share against the cap. The sixth devnet run, against the banded dollar sale (PBAND2), ended:

proof    : the largest wallet holds 16.93 percent of the 6229218448 raw units sold, against a cap worth 17.66 percent of them
cost     : 0.011317 SOL, after 0.033344 SOL came back from the attacking wallets

9 attacks run, 8 refused as expected, 1 allowed as expected, 1 not applicable, 0 off the standard

and exited 0. The same run against the credential sale (PVRFD), with --mint 7ixMAMUmysN4qsCpthdznhq7aRbiCNB5Xeg3X9vBSLWn, ended 10 attacks run, 8 refused as expected, 2 allowed as expected, 1 not applicable, 0 off the standard and exited 0, and the seventh run against PAAPLX, with --mint 8FaBUEjMfkpYAzZLmLKwYHxd15BNC1ZBFkgTgN2WCQvK, ended 9 attacks run, 8 refused as expected, 1 allowed as expected, 1 not applicable, 0 off the standard. npm run launch, npm run seed, and npm run graduate set up and advance a sale the same way; npm run refresh-price refreshes the oracle quote the band reads.

5. What this does not do

From docs/security/threat-model.md, "Non-goals, named", in plain words.

  • One person can still hold many wallets. The cap is per wallet, and a cap is only as strong as the identity check behind the approved-buyer list. The issuer list mode has no identity check at all; the credential mode is as good as whoever issues the credential.
  • After a sale graduates, Pangu's rules are gone for good. DBC removes the hook in the same trade that completes the curve. That is by design.
  • When a sale's offering period ends, every rule lifts too, cap and approvals included, whether or not the curve filled. That is by design as well: a curve that never fills should not lock its buyers in. The end is fixed when the sale opens and nobody can move it. PBAND2's and PAAPLX's fall on 7 October 2026, after judging, so the lift has been proven by tests/offering.ts and the fork test and not yet seen on devnet.
  • The dollar list behind the ceiling is a list of exact addresses. It does not check that a listed stablecoin still holds its peg.
  • The freeze check covers the issuer's own key. A freeze authority held by another wallet the issuer controls cannot be told apart from a stablecoin issuer's.
  • Pangu never checks that a token is actually backed by real shares. That is the issuer's claim, not something this program can see on chain.
  • The upgrade key holder (the team, stated openly) can change the program while a sale is live, unless the key is later given up. On mainnet the plan is a two of three Squads multisig, then a freeze after an independent audit; that trade-off and its reasons are in docs/security/threat-model.md and docs/deploy/mainnet.md.
  • A stock token such as AAPLx carries a permanent delegate that can move it out of any account, including the pool's vault, and a pause switch that can stop every trade. Those powers belong to the stock token's own issuer, and Pangu cannot defend against them. Any sale priced in a stock token discloses this.
  • The cap limits what a wallet receives, not its live share of what remains. When other holders sell back, one wallet's share of tokens still held can rise above the cap's share of the original sale. The app reports share of tokens sold, not share of tokens held, and says so.
  • Front-running between buyers who are all under the cap is not addressed here; DBC's own decaying fee is the tool for that, not Pangu.
  • The price band first shipped on Switchboard On-Demand (forgery attempts refused on devnet, see docs/security/attacks/forged-quote.md), then was rebuilt on Pyth after Switchboard announced it shuts down on 25 September 2026. Those forgery attempts ran against the retired Switchboard build. The banded devnet runs from the fourth run on, in docs/measurements/devnet-run.md, are on the Pyth build.
  • The Pyth price key the team holds is trial access, extended by Pyth support to 5 October 2026, which covers judging but not a production issuer. A real issuer would need its own Pyth key.
  • PriceOutsideBand (a buy refused for going above the ceiling) has landed live on devnet only on the team's own demo sales, PBAND in the fifth run, PBAND2 in the sixth and PAAPLX in the seventh, whose holders the team seeded, not on a sale with outside trading volume. See docs/measurements/devnet-run.md and the fork test in docs/measurements/sdk-fork-test.md. Nothing has run on mainnet.
  • The demo credential used for the attestation approval mode is self-issued. No real KYC or verifier company has published a credential address for Pangu to point at, so the demo issues its own and says so plainly in the app and here.
  • Mainnet deploy has not happened. Every "mainnet: ready, not deployed" cell in section 1 is honest about that. The mainnet build, its deploy, sales in USDC and AAPLx, the Squads handover and the mainnet build of the app have run on a forked copy of mainnet, not on mainnet itself.

6. Pages read for this rubric

ARCHITECTURE.md, docs/security/threat-model.md, docs/measurements/devnet-run.md, docs/measurements/fork-test.md, docs/measurements/credential-mode.md, docs/measurements/price-band.md, docs/measurements/price-band-pyth.md, docs/measurements/random-sequences.md, docs/measurements/review-fixes.md, docs/measurements/sdk-fork-test.md, docs/measurements/mainnet-rehearsal.md, docs/security/attacks/forged-quote.md, docs/measurements/test-counts.md, docs/deployments.md, docs/deploy/mainnet.md, docs/deploy/vercel.md, packages/web/lab-evidence/launch-devnet.txt, metadata-devnet.txt, sale-devnet.txt, verify-devnet.txt, portfolio-devnet.txt, network-fork.txt, network-devnet-launch-shots.txt, packages/scripts/package.json, .env.example, scripts/wsl/test.sh, scripts/wsl/fork-test.sh.