{
  "project": "P/V2",
  "read_as": "pump.fun v2",
  "chain": "solana",
  "runtime": "anchor 0.31, rust, token-2022 and spl-token both supported at mint creation",
  "program_display_name": "P/V2",
  "explorer_name_rendering": {
    "rule": "the deployed program must show the human readable name P/V2 on every explorer",
    "steps": [
      "security.txt embedded in the program binary through solana-security-txt with name set to P/V2, project_url, source_code and contacts filled with real values at deploy",
      "anchor idl init on mainnet right after deploy so the IDL account exists and explorers resolve instruction names",
      "program metadata account written through the metadata program with name P/V2 so solscan and solana.fm label the program id",
      "verified build submitted through solana-verify so the explorer shows the verified badge next to the name"
    ]
  },
  "thesis": "the first pump.fun used one fixed constant product curve, one fixed migration line and one fixed fee. P/V2 is a launch standard where the curve, the fee, the reserve and the migration are all state machines that only advance when a trade lands. nothing in P/V2 runs on a clock. every transition is caused by a fill.",
  "no_clock_rule": {
    "statement": "no instruction reads Clock::get() for logic. no epochs, no streaks, no snapshots, no scheduled runs, no cooldowns measured in slots or seconds.",
    "what_advances_state": [
      "a buy fill",
      "a sell fill",
      "a reserve rung being crossed by a fill",
      "a migration being triggered by a fill",
      "a holder count change caused by a fill"
    ],
    "trade_index": "every fill increments a u64 trade_index on the pool. all gates, windows and counters are expressed in trade_index deltas, never in time."
  },
  "accounts": {
    "Global": {
      "seeds": ["global"],
      "fields": {
        "authority": "pubkey, immutable after init",
        "fee_recipient_vault": "pubkey, program owned wsol vault",
        "default_curve_profile": "u8, index into CurveProfile set",
        "default_fee_profile": "u8",
        "migration_target": "enum { raydium_cpmm, meteora_damm_v2, pumpswap_style_internal }",
        "launch_count": "u64",
        "total_fills": "u64",
        "paused": "bool, only blocks create, never blocks sells"
      }
    },
    "CurveProfile": {
      "seeds": ["curve", "profile_index:u8"],
      "fields": {
        "segments": "Vec<CurveSegment> up to 8",
        "virtual_sol_reserve_initial": "u64 lamports",
        "virtual_token_reserve_initial": "u64 base units",
        "real_token_supply_for_curve": "u64, tokens that live on the curve",
        "migration_token_reserve_floor": "u64, when real token reserve drops below this the pool is eligible to migrate",
        "migration_requires_fill": "bool, always true"
      },
      "CurveSegment": {
        "start_token_reserve": "u64, segment active while real token reserve is above this",
        "kind": "enum { constant_product, linear_price, exponential_price, sigmoid_price, step_price }",
        "k_scale_bps": "u32, multiplies the invariant k when the segment activates so the price surface is continuous at the boundary",
        "steepness_q32": "u64, fixed point parameter used by exponential and sigmoid kinds",
        "midpoint_token_reserve": "u64, used by sigmoid only",
        "step_size_tokens": "u64, used by step only",
        "step_bump_bps": "u32, used by step only"
      }
    },
    "FeeProfile": {
      "seeds": ["fee", "profile_index:u8"],
      "fields": {
        "base_buy_fee_bps": "u16",
        "base_sell_fee_bps": "u16",
        "fee_curve": "enum { flat, volume_decay, pressure_reactive, reserve_weighted }",
        "fee_floor_bps": "u16",
        "fee_ceiling_bps": "u16",
        "split": {
          "protocol_bps": "u16",
          "creator_bps": "u16",
          "reserve_bps": "u16, fed into the pool's own ReserveVault",
          "buyback_bps": "u16, fed into the BuybackVault",
          "holder_bps": "u16, fed into the HolderDistributor",
          "sum_rule": "all five must sum to 10000 exactly or init fails"
        }
      }
    },
    "Launch": {
      "seeds": ["launch", "mint"],
      "fields": {
        "mint": "pubkey",
        "creator": "pubkey",
        "curve_profile": "u8",
        "fee_profile": "u8",
        "virtual_sol_reserve": "u64",
        "virtual_token_reserve": "u64",
        "real_sol_reserve": "u64",
        "real_token_reserve": "u64",
        "trade_index": "u64",
        "buy_count": "u64",
        "sell_count": "u64",
        "active_segment": "u8",
        "reserve_rung": "u8, which ReserveVault rung has been reached",
        "state": "enum { curve, migrating, migrated, stalled }",
        "stall_trade_index": "u64, the trade_index at which the pool last changed state",
        "holder_count": "u32, maintained by the HolderIndex on every fill",
        "largest_holder_bps": "u16, share of curve supply held by the largest wallet, updated on every fill",
        "creator_withdrawn_lamports": "u64",
        "protocol_withdrawn_lamports": "u64",
        "bump": "u8"
      }
    },
    "ReserveVault": {
      "seeds": ["reserve", "mint"],
      "purpose": "a wsol vault owned by the program that grows from the reserve_bps slice of every fee and is spent only by the program itself, never withdrawable by any human key",
      "fields": {
        "lamports": "u64 mirror of the token account balance",
        "rungs": "[ReserveRung; 12]",
        "rung_reached": "u8",
        "deployed_lamports": "u64, total ever pushed into the curve as buy pressure",
        "last_deploy_trade_index": "u64"
      },
      "ReserveRung": {
        "threshold_lamports": "u64, vault balance that unlocks this rung",
        "action": "enum { inject_virtual_sol, buy_and_burn, buy_and_hold, lower_sell_fee, raise_migration_floor, seed_migration_liquidity }",
        "amount_bps_of_vault": "u16, how much of the vault the action spends",
        "fires_on": "the first fill after the threshold is crossed, inside that fill's instruction, never by a crank"
      }
    },
    "BuybackVault": {
      "seeds": ["buyback", "mint"],
      "purpose": "accumulates the buyback_bps slice. spent only by the program during sell fills.",
      "fields": {
        "lamports": "u64",
        "mode": "enum { counter_sell, dip_absorb, post_migration_pool_buy }",
        "counter_sell_bps": "u16, fraction of each sell's sol output that the vault immediately re-buys inside the same instruction",
        "absorb_trigger_price_drop_bps": "u16, if a single sell moves spot price down by more than this the vault buys until the drop is reduced to this value or the vault is empty",
        "tokens_held": "u64, tokens the vault bought and holds",
        "tokens_burned": "u64"
      }
    },
    "HolderDistributor": {
      "seeds": ["holders", "mint"],
      "purpose": "the holder_bps slice is paid out to holders by fill, not by schedule",
      "fields": {
        "lamports_accrued": "u64",
        "reward_per_token_q64": "u128, global accumulator, increases on every fill by fee_slice * 2^64 / circulating_curve_tokens",
        "distribution_trigger": "every fill, the accumulator moves. a holder realises their share when they next buy, sell, or call claim. no epoch.",
        "weighting": "linear in tokens held with a concave cap: any wallet above largest_holder_cap_bps of supply earns as if it held exactly the cap"
      }
    },
    "HolderPosition": {
      "seeds": ["position", "mint", "owner"],
      "fields": {
        "owner": "pubkey",
        "tokens": "u64 mirror",
        "reward_debt_q64": "u128",
        "claimable_lamports": "u64",
        "first_trade_index": "u64, trade_index at which this wallet first bought",
        "fill_count": "u32"
      }
    },
    "HolderIndex": {
      "seeds": ["index", "mint"],
      "purpose": "a compact on chain registry of nonzero holders so holder_count and largest_holder_bps are exact and computed inside fills",
      "fields": {
        "count": "u32",
        "largest": "pubkey",
        "largest_tokens": "u64",
        "bloom": "[u8; 2048], membership filter so the program knows whether to increment count on a first buy without scanning"
      }
    },
    "MigrationRecord": {
      "seeds": ["migration", "mint"],
      "fields": {
        "trigger_trade_index": "u64",
        "triggering_trader": "pubkey, the wallet whose fill crossed the floor",
        "sol_moved": "u64",
        "tokens_moved": "u64",
        "lp_mint": "pubkey",
        "lp_locked": "bool, always true",
        "lp_lock_authority": "pubkey, the program's own pda so no key can ever pull liquidity",
        "post_migration_fee_route": "enum { to_reserve_vault, to_buyback_vault, to_holder_distributor, three_way_split }"
      }
    }
  },
  "instructions": {
    "initialize_global": {
      "signer": "authority",
      "writes": ["Global"],
      "notes": "runs once. sets fee vault, default profiles and migration target."
    },
    "register_curve_profile": {
      "signer": "authority",
      "writes": ["CurveProfile"],
      "validation": [
        "segments must be sorted by start_token_reserve descending",
        "the price at the boundary between two adjacent segments must match to within 1 basis point after k_scale_bps is applied, the program computes both sides and rejects a discontinuity",
        "the last segment's start_token_reserve must be at or below migration_token_reserve_floor"
      ]
    },
    "register_fee_profile": {
      "signer": "authority",
      "writes": ["FeeProfile"],
      "validation": ["split sums to 10000", "fee_floor_bps <= base fees <= fee_ceiling_bps"]
    },
    "create": {
      "signer": "creator",
      "writes": ["Launch", "ReserveVault", "BuybackVault", "HolderDistributor", "HolderIndex", "mint", "curve token account", "metadata"],
      "args": {
        "name": "string",
        "symbol": "string, stored in metadata only, never surfaced by any P/V2 site",
        "uri": "string",
        "curve_profile": "u8",
        "fee_profile": "u8",
        "initial_buy_lamports": "u64, optional, the creator's first buy lands as trade_index 1 under the same fee rules as anyone else",
        "creator_fee_lock": "bool, if true the creator slice of fees is routed into the ReserveVault instead of the creator and cannot be undone"
      },
      "effects": [
        "mint created with mint authority set to the Launch pda then revoked after the curve supply is minted",
        "freeze authority null from the first instruction",
        "real_token_supply_for_curve minted into the curve token account, the migration remainder minted into a program owned escrow",
        "virtual reserves copied from the CurveProfile",
        "trade_index set to 0"
      ]
    },
    "buy": {
      "signer": "trader",
      "args": {
        "sol_in_lamports": "u64",
        "min_tokens_out": "u64"
      },
      "order_of_operations": [
        "1 load Launch, assert state == curve",
        "2 compute fee_bps from the FeeProfile fee_curve using the current pool pressure (see fee_engine)",
        "3 fee = sol_in * fee_bps / 10000, net = sol_in - fee",
        "4 split fee five ways per FeeProfile.split and move each slice to its vault",
        "5 tokens_out = curve_out(net) using the active CurveSegment, walking across segment boundaries if the fill crosses one, each boundary rescales k by k_scale_bps",
        "6 assert tokens_out >= min_tokens_out",
        "7 transfer tokens to trader, update real and virtual reserves",
        "8 trade_index += 1, buy_count += 1",
        "9 HolderIndex update: if trader not in bloom, insert and count += 1. if trader balance > largest_tokens, update largest",
        "10 HolderDistributor accumulator += holder_slice * 2^64 / circulating_curve_tokens",
        "11 HolderPosition for trader: settle claimable from accumulator delta, then update reward_debt",
        "12 ReserveVault rung check: if lamports >= rungs[rung_reached].threshold, execute that rung's action inside this instruction, rung_reached += 1, repeat while the next threshold is also crossed",
        "13 migration check: if real_token_reserve <= migration_token_reserve_floor, set state = migrating, write MigrationRecord with this trader as triggering_trader, and emit MigrationArmed. the migrate instruction may then be called by anyone and is also attempted inline if compute allows",
        "14 emit Fill event with every field needed to reconstruct the pool from events alone"
      ]
    },
    "sell": {
      "signer": "trader",
      "args": {
        "tokens_in": "u64",
        "min_sol_out": "u64"
      },
      "order_of_operations": [
        "1 load Launch, assert state == curve",
        "2 gross_sol = curve_in(tokens_in) across segments",
        "3 fee_bps from fee_engine, sell side",
        "4 fee split five ways",
        "5 BuybackVault.counter_sell: the vault spends counter_sell_bps of the trader's net sol output buying tokens on the curve inside this same instruction, after the sell has been applied, so the price the seller got is honored but the price the next trader sees is partially restored",
        "6 BuybackVault.dip_absorb: if the sell moved spot down more than absorb_trigger_price_drop_bps the vault keeps buying in fixed increments until the drop is reduced below the trigger or the vault is empty, bounded by a max of 8 increments per instruction for compute",
        "7 tokens the vault buys go to tokens_held or are burned depending on the rung state of the ReserveVault",
        "8 HolderIndex update: if trader balance hits zero, remove from count. if trader was largest, rescan the top 16 recorded positions kept in a small on chain heap to find the new largest",
        "9 HolderDistributor and HolderPosition settle as in buy",
        "10 trade_index += 1, sell_count += 1",
        "11 ReserveVault rung check as in buy",
        "12 emit Fill"
      ]
    },
    "claim_holder_rewards": {
      "signer": "owner",
      "effect": "settles HolderPosition against the accumulator and pays claimable_lamports as wsol to the owner. callable at any trade_index. does not advance trade_index."
    },
    "migrate": {
      "signer": "anyone",
      "precondition": "Launch.state == migrating",
      "effects": [
        "pull real_sol_reserve minus the protocol migration fee into the migration target pool together with the escrowed migration token remainder",
        "ReserveVault rung action seed_migration_liquidity, if reached, adds its amount to the pool on top so the migrated price is at or above the final curve price, never below",
        "mint LP, send LP to the program's lock pda, write lp_locked = true",
        "set state = migrated",
        "post migration, the FeeProfile keeps running: the migration pool is created with a fee hook if the target supports it, routing pool fees into the vaults per post_migration_fee_route, and the BuybackVault switches to post_migration_pool_buy mode where it buys from the pool on every sell observed through the pool's own fee hook"
      ]
    },
    "withdraw_creator_fees": {
      "signer": "creator",
      "effect": "pays the creator slice accrued since the last withdrawal. if creator_fee_lock was set at create this instruction always fails."
    },
    "withdraw_protocol_fees": {
      "signer": "authority",
      "effect": "pays the protocol slice to fee_recipient_vault."
    },
    "stall_recover": {
      "signer": "anyone",
      "precondition": "state == stalled",
      "notes": "a pool enters stalled only if a migration attempt fails at the target (for example the target program rejects the pool creation). stall_recover retries the migration with the same amounts. nothing else changes."
    }
  },
  "curve_math": {
    "constant_product": {
      "invariant": "k = virtual_sol_reserve * virtual_token_reserve",
      "buy": "tokens_out = virtual_token_reserve - k / (virtual_sol_reserve + net_sol)",
      "sell": "sol_out = virtual_sol_reserve - k / (virtual_token_reserve + tokens_in)",
      "spot_price_lamports_per_token": "virtual_sol_reserve / virtual_token_reserve"
    },
    "linear_price": {
      "price(r)": "p0 + slope * (R0 - r), where r is the real token reserve, R0 the segment start, slope derived from steepness_q32 / 2^32",
      "buy": "closed form, solve the quadratic in tokens_out for the integral of price from r to r - tokens_out equal to net_sol, take the positive root, round down",
      "sell": "same integral in the other direction, round down"
    },
    "exponential_price": {
      "price(r)": "p0 * exp(a * (R0 - r) / R0), a = steepness_q32 / 2^32",
      "buy": "closed form through the natural log, computed with a 64.64 fixed point log and exp table in the program, error bound checked against the invariant after every fill and the fill reverts if the recomputed k drifts more than 1 part in 10^9",
      "sell": "closed form"
    },
    "sigmoid_price": {
      "price(r)": "p_low + (p_high - p_low) / (1 + exp(-a * (m - r))), m = midpoint_token_reserve",
      "integration": "no closed form for the integral in fixed point that is cheap enough, so the fill is computed by 24 step adaptive Simpson integration with a bisection on tokens_out, max 40 iterations, worst case within compute budget measured at build time",
      "use": "the segment that sits right before the migration floor so the last tokens on the curve get expensive fast and migration lands high"
    },
    "step_price": {
      "price(r)": "p0 * (1 + step_bump_bps / 10000) ^ floor((R0 - r) / step_size_tokens)",
      "buy": "walk the steps, each step is constant price, sum until net_sol is spent",
      "use": "a visible rung look for sites that want to show the curve as a staircase table"
    },
    "segment_boundary_continuity": "when a fill crosses from segment i to i+1 the program computes price_i at the boundary reserve and price_i+1 at the same reserve. if they differ by more than 1 bps the profile was invalid at registration, so this cannot happen at fill time, but the fill still asserts it as a defensive check."
  },
  "fee_engine": {
    "flat": "fee_bps = base",
    "volume_decay": {
      "formula": "fee_bps = max(floor, base * decay ^ trade_index), decay = 0.9995 in q32",
      "meaning": "the more fills a pool has taken the cheaper it gets to trade it. early fills pay the most."
    },
    "pressure_reactive": {
      "pressure": "p = (buy_count - sell_count) over the last 64 fills, kept as a ring buffer of 64 bits on the Launch account, one bit per fill, 1 for buy 0 for sell",
      "formula_buy": "fee_bps = clamp(base + ceiling_delta * max(0, p - 32) / 32, floor, ceiling)",
      "formula_sell": "fee_bps = clamp(base + ceiling_delta * max(0, 32 - p) / 32, floor, ceiling)",
      "meaning": "when the last 64 fills are mostly buys, buying gets more expensive and selling gets cheaper. when they are mostly sells, selling gets more expensive and buying gets cheaper. the window is a fill count, not a time window."
    },
    "reserve_weighted": {
      "formula": "fee_bps = clamp(base * migration_token_reserve_floor / real_token_reserve, floor, ceiling)",
      "meaning": "fees rise as the pool approaches migration, feeding the ReserveVault hardest right before the pool leaves the curve"
    }
  },
  "reserve_rung_actions": {
    "inject_virtual_sol": "adds amount to virtual_sol_reserve without adding real sol, which lifts the spot price for every subsequent fill. the lamports spent are moved to the migration escrow so they still arrive in the pool at migration. the curve gets steeper, not fake.",
    "buy_and_burn": "the vault buys on the curve with amount and burns the tokens. supply on the curve falls, real_token_reserve falls, the migration floor is reached sooner.",
    "buy_and_hold": "the vault buys and keeps the tokens under the program pda. these tokens are added to the migration pool at migrate, deepening the pool.",
    "lower_sell_fee": "permanently reduces base_sell_fee_bps for this launch by amount_bps_of_vault interpreted as basis points, floored at fee_floor_bps. a pool that has banked a reserve rewards exits.",
    "raise_migration_floor": "increases migration_token_reserve_floor by amount interpreted as tokens, so migration happens earlier and the pool goes to the target with more sol per token.",
    "seed_migration_liquidity": "earmarks amount for the migrate instruction, which adds it to the pool on top of the curve's real sol."
  },
  "holder_distribution": {
    "accumulator": "reward_per_token_q64 += holder_slice_lamports << 64 / circulating_curve_tokens, on every fill",
    "settle": "owed = tokens * reward_per_token_q64 >> 64 - reward_debt_q64, then reward_debt_q64 = tokens * reward_per_token_q64 >> 64",
    "cap": "if tokens > largest_holder_cap_bps * curve_supply / 10000, tokens is replaced by the cap in the settle formula. the excess share is left in the accumulator and so flows to everyone else.",
    "paid_in": "wsol, claimable through claim_holder_rewards or auto settled on the holder's next buy or sell"
  },
  "events": {
    "Fill": {
      "fields": [
        "mint", "trader", "side", "trade_index", "sol_gross", "fee_lamports", "fee_bps_applied", "tokens", "virtual_sol_reserve_after", "virtual_token_reserve_after", "real_sol_reserve_after", "real_token_reserve_after", "active_segment_after", "spot_price_after", "holder_count_after", "largest_holder_bps_after", "reserve_vault_lamports_after", "buyback_vault_lamports_after", "buyback_tokens_bought_in_this_fill", "rungs_fired_in_this_fill"
      ]
    },
    "RungFired": ["mint", "rung_index", "action", "lamports_spent", "trade_index"],
    "MigrationArmed": ["mint", "trade_index", "triggering_trader", "real_sol_reserve"],
    "Migrated": ["mint", "pool", "lp_mint", "sol_moved", "tokens_moved", "trade_index"],
    "HolderSettled": ["mint", "owner", "lamports", "trade_index"],
    "reconstruction_rule": "the Fill event carries every after state, so any indexer can rebuild the complete pool history from events without reading accounts"
  },
  "security": {
    "mint_authority": "revoked at create after supply is minted",
    "freeze_authority": "never set",
    "lp": "minted to a program pda with no withdraw instruction in the program. there is no code path that moves LP.",
    "reserve_and_buyback_vaults": "no instruction transfers lamports out of these vaults to any signer. the only outflows are curve buys and migration seeding executed by the program.",
    "upgrade_authority": "set to a squads multisig at deploy, then moved to null once the first ten launches have migrated cleanly. the program is then immutable.",
    "reentrancy": "all cross program calls happen after state is written and every balance is re read after the cpi returns",
    "integer_safety": "every arithmetic op is checked, every fixed point op has a documented bound, the exp and log tables are tested against a reference at build time",
    "front_running": "no time based gates exist to race. the only sequence dependent behaviour is the pressure ring buffer, which is visible on chain and symmetric for every trader."
  },
  "default_profiles_shipped_at_deploy": {
    "curve_profile_0": {
      "name": "classic",
      "segments": [
        { "kind": "constant_product", "start_token_reserve": "full curve supply", "k_scale_bps": 10000 }
      ],
      "notes": "one constant product segment. behaves like the first pump.fun curve so launches can opt out of complexity."
    },
    "curve_profile_1": {
      "name": "ramp",
      "segments": [
        { "kind": "constant_product", "start_token_reserve": "full curve supply", "k_scale_bps": 10000 },
        { "kind": "exponential_price", "start_token_reserve": "40 percent of curve supply", "k_scale_bps": 10000, "steepness": "set so the price doubles over the segment" },
        { "kind": "sigmoid_price", "start_token_reserve": "12 percent of curve supply", "k_scale_bps": 10000, "midpoint": "6 percent of curve supply" }
      ],
      "notes": "constant product for discovery, exponential for the middle, sigmoid for the run into migration"
    },
    "curve_profile_2": {
      "name": "stair",
      "segments": [
        { "kind": "step_price", "start_token_reserve": "full curve supply", "step_size": "2 percent of curve supply", "step_bump_bps": 350 }
      ],
      "notes": "fifty visible steps from launch to migration"
    },
    "fee_profile_0": {
      "name": "flat",
      "base_buy_fee_bps": 100,
      "base_sell_fee_bps": 100,
      "fee_curve": "flat",
      "split": { "protocol_bps": 2500, "creator_bps": 2500, "reserve_bps": 2500, "buyback_bps": 1500, "holder_bps": 1000 }
    },
    "fee_profile_1": {
      "name": "reactive",
      "base_buy_fee_bps": 100,
      "base_sell_fee_bps": 100,
      "fee_curve": "pressure_reactive",
      "fee_floor_bps": 40,
      "fee_ceiling_bps": 400,
      "split": { "protocol_bps": 2000, "creator_bps": 2000, "reserve_bps": 3000, "buyback_bps": 2000, "holder_bps": 1000 }
    },
    "fee_profile_2": {
      "name": "reserve heavy",
      "base_buy_fee_bps": 125,
      "base_sell_fee_bps": 125,
      "fee_curve": "reserve_weighted",
      "fee_floor_bps": 125,
      "fee_ceiling_bps": 600,
      "split": { "protocol_bps": 1500, "creator_bps": 1500, "reserve_bps": 4500, "buyback_bps": 2000, "holder_bps": 500 }
    },
    "reserve_rungs_default": [
      { "threshold": "0.5 percent of migration sol", "action": "lower_sell_fee", "amount": "10 bps" },
      { "threshold": "1 percent of migration sol", "action": "buy_and_burn", "amount_bps_of_vault": 5000 },
      { "threshold": "2 percent of migration sol", "action": "inject_virtual_sol", "amount_bps_of_vault": 5000 },
      { "threshold": "4 percent of migration sol", "action": "buy_and_hold", "amount_bps_of_vault": 5000 },
      { "threshold": "7 percent of migration sol", "action": "raise_migration_floor", "amount": "1 percent of curve supply" },
      { "threshold": "10 percent of migration sol", "action": "seed_migration_liquidity", "amount_bps_of_vault": 10000 }
    ],
    "buyback_default": {
      "mode": "counter_sell",
      "counter_sell_bps": 1500,
      "absorb_trigger_price_drop_bps": 300
    },
    "holder_cap_default_bps": 300
  },
  "site_spec": {
    "name_in_header": "P/V2",
    "ticker_rule": "the token symbol is never shown anywhere on the site",
    "market_cap_rule": "the words market cap and mc never appear. the live value is labeled value.",
    "data_source": "birdeye price polled every 1s through a supabase edge function, usd value = birdeye usd price times circulating supply, no sol usd conversion anywhere, candles fetched on mount only",
    "dev_panel": "top right corner button on the lovable preview host only, opens an overlay where the program id and a launch mint are pasted and saved, every readout derives from those, no passphrase",
    "pages": {
      "main": "one viewport, no scroll, narrow centered column, wordmark top left, underlined slash nav, X button in the nav with no destination set, live value with red green on down up, clean minimal value chart, then the list of every launch deployed through the program with its live value and a clean chart each",
      "curve": "the active launch's curve drawn as a computed table of segment boundaries and prices, plus an svg of the price surface drawn from the segment math, no axis legend plot",
      "reserve": "the ReserveVault rung table with reached rungs marked, each rung's action explained in full prose, the RungFired event list read from chain",
      "fills": "the Fill event stream read from chain, static blocks, only values swap in place, no auto advance",
      "holders": "holder count, largest holder share, the accumulator value, and the concave cap explained",
      "docs": "one long scrolling page with every chapter present: thesis, no clock rule, accounts, every instruction's order of operations, every curve kind with its formula, the fee engine, the rung actions, holder distribution, migration, security, verification"
    },
    "verification_links": "program id on solscan and solana.fm, verified build page, idl, the migration pool and locked lp mint for the active launch",
    "rules_carried": [
      "no fake values ever, slots stay blank until a mint is configured",
      "no footer",
      "no socials",
      "no sliders toggles filters or collapsibles, the site is a readout",
      "nothing moves on its own",
      "tonal hierarchy of greys, not one flat white",
      "small mono text, dense pages, every page filled"
    ]
  },
  "verification_checklist": [
    "explorer shows the name P/V2 next to the program id",
    "IDL account exists and instruction names resolve on solscan",
    "no instruction reads Clock::get() for logic, grep the source for Clock and sysvar::clock and find only none",
    "fee split sums to 10000 for every profile",
    "every curve profile passes the boundary continuity check at registration",
    "mint authority revoked and freeze authority null on every launch",
    "no code path moves LP or withdraws from ReserveVault or BuybackVault to a signer",
    "a full pool history can be rebuilt from Fill events alone",
    "a sell that drops spot by more than absorb_trigger_price_drop_bps is followed inside the same transaction by vault buys",
    "a rung threshold crossed by a fill fires inside that same fill",
    "migration lands at or above the final curve price"
  ]
}
